From e8c3955c7f6ae54b505d27d8f48f95e4ebe650c2 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 2 Mar 2023 22:21:45 -0500 Subject: [PATCH 001/127] Start on changes to enable workflows --- README.md | 96 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 9ff0e670..bac34615 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# UCAN Invocation Specification v0.1.1 +# UCAN Invocation Specification v0.2.0 ## Editors @@ -195,11 +195,10 @@ An [Effect] are the instruction to the [Executor] to run set of [Task]s concurre ```ipldsch type Task struct { - on URI - call Ability - input {String : Any} - - nnc string + obj URI + call Ability + args {String : Any} + nnc string } type URI string @@ -208,24 +207,52 @@ type Ability string type Authorization struct { # Authorization is denoted by the set of links been authorized scope [&Any] + # Scope signed by the invoker s VarSig } -type Invocation struct { +type Context struct { v SemVer - run &Task + meta {String : Any} + prf [&UCAN] # Receipt of the invocation that caused this invocation cause optional &Invocation +} + +type Invocation struct { + ctx &Context # Task authorization. auth &Authorization +} - meta {String : Any} - - prf [&UCAN] +{ + "ctx": &{ + "v": "0.1.0", + "run": &{ + "obj" "mailto://alice@example.com", + "call": "msg/send", + "input": { + "to": { + "await/ok": {"/": "bafy...getMailingList"} + }, + "subject": "hello", + "body": "world" + } + }, + "meta": { + "gas": 1000, + "timeout": 5000 + }, + "prf": [...] + }, + "auth": &{ + "scope": [...], + "s": "0xabcdef" + } } type SemVer string @@ -286,7 +313,7 @@ Using the JavaScript analogy from the introduction, a Task is similar to wrappin ```json { - "on": "mailto:alice@example.com", + "obj" "mailto:alice@example.com", "call": "msg/send", "input": { "to": [ @@ -314,11 +341,11 @@ Later, when we explore promise [pipelines], this also includes capturing the pro ```json { "bafy...getMailingList": { - "on": "https://exmaple.com/mailinglist", + "obj" "https://exmaple.com/mailinglist", "call": "crud/read" }, "bafy...sendEmail": { - "on": "mailto://alice@example.com", + "obj" "mailto://alice@example.com", "call": "msg/send", "input": { "to": { @@ -347,7 +374,7 @@ const sendEmail = msg.send("mailto://alice@example.com", { ```ipldsch type Task struct { - on URI + obj URI call Ability input {String : Any} nnc string @@ -389,9 +416,9 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "on": "https://example.com/blog/posts", + "obj" "https://example.com/blog/posts", "call": "crud/create", - "input": { + "args": { "headers": { "content-type": "application/json" }, @@ -412,7 +439,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "on": "mailto:akiko@example.com", + "obj" "mailto:akiko@example.com", "call": "msg/send", "input": { "to": [ @@ -429,7 +456,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "on": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", + "obj" "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", "call": "wasm/run", "input": { "func": "add_one", @@ -503,6 +530,7 @@ type Invocation struct { v SemVer run &Task + # Receipt of the invocation that caused this invocation cause optional &Invocation @@ -552,7 +580,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt ```json { "bafy...createBlogPost": { - "on": "https://example.com/blog/posts", + "obj" "https://example.com/blog/posts", "call": "crud/create", "input": { "headers": { @@ -603,7 +631,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt ```json { "bafy...createBlogPostTask": { - "on": "https://example.com/blog/posts", + "obj" "https://example.com/blog/posts", "call": "crud/create", "input": { "headers": { @@ -621,7 +649,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt } }, "bafy...sendEmailTask": { - "on": "mailto:akiko@example.com", + "obj" "mailto:akiko@example.com", "call": "msg/send", "input": { "to": [ @@ -683,7 +711,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt ```json { "bafy...updateDnsTask": { - "on": "dns:example.com?TYPE=TXT", + "obj" "dns:example.com?TYPE=TXT", "call": "crud/update", "input": { "value": "hello world" @@ -1031,7 +1059,7 @@ For example, consider the following invocation batch: ```json { "bafy...createBlogPostTask": { - "on": "https://example.com/blog/posts", + "obj" "https://example.com/blog/posts", "call": "crud/create", "input": { "payload": { @@ -1041,11 +1069,11 @@ For example, consider the following invocation batch: } }, "bafy...getBlogEditorsTask": { - "on": "https://example.com/users/editors", + "obj" "https://example.com/users/editors", "call": "crud/read" }, "bafy...sendEmailTask": { - "on": "mailto:akiko@example.com", + "obj" "mailto:akiko@example.com", "call": "msg/send", "input": { "to": { @@ -1184,14 +1212,14 @@ flowchart BR ```json { "bafy...updateDnsTask": { - "on": "dns:example.com?TYPE=TXT", + "obj" "dns:example.com?TYPE=TXT", "call": "crud/update", "input": { "value": "hello world" } }, "bafy...sendBobEmailTask": { - "on": "mailto://alice@example.com", + "obj" "mailto://alice@example.com", "call": "msg/send", "input": { "to": "bob@example.com", @@ -1204,7 +1232,7 @@ flowchart BR } }, "bafy...sendCarolEmailTask": { - "on": "mailto://alice@example.com", + "obj" "mailto://alice@example.com", "call": "msg/send", "input": { "to": "carol@example.com", @@ -1217,7 +1245,7 @@ flowchart BR } }, "bafy...updateReportTask": { - "on": "https://example.com/report", + "obj" "https://example.com/report", "call": "crud/update", "input": { "payload": { @@ -1357,14 +1385,14 @@ flowchart TB ```json { "bafy...updateDnsTask": { - "on": "dns:example.com?TYPE=TXT", + "obj" "dns:example.com?TYPE=TXT", "call": "crud/update", "input": { "value": "hello world" } }, "bafy...sendBobEmailTask": { - "on": "mailto://alice@example.com", + "obj" "mailto://alice@example.com", "call": "msg/send", "input": { "to": "bob@example.com", @@ -1425,7 +1453,7 @@ flowchart TB ```json { "bafy...emailCarolTask": { - "on": "mailto://alice@example.com", + "obj" "mailto://alice@example.com", "call": "msg/send", "input": { "to": "carol@example.com", @@ -1438,7 +1466,7 @@ flowchart TB } }, "bafy...updateReportTask": { - "on": "https://example.com/report", + "obj" "https://example.com/report", "call": "crud/update", "input": { "payload": { From d4764264ba28a7e28f6d0701f8990b898b0ef505 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 2 Mar 2023 22:24:18 -0500 Subject: [PATCH 002/127] Put schema back --- README.md | 36 ++---------------------------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index bac34615..a3f1da75 100644 --- a/README.md +++ b/README.md @@ -212,49 +212,17 @@ type Authorization struct { s VarSig } -type Context struct { +type Invocation struct { v SemVer run &Task meta {String : Any} prf [&UCAN] + auth &Authorization # Receipt of the invocation that caused this invocation cause optional &Invocation } -type Invocation struct { - ctx &Context - - # Task authorization. - auth &Authorization -} - -{ - "ctx": &{ - "v": "0.1.0", - "run": &{ - "obj" "mailto://alice@example.com", - "call": "msg/send", - "input": { - "to": { - "await/ok": {"/": "bafy...getMailingList"} - }, - "subject": "hello", - "body": "world" - } - }, - "meta": { - "gas": 1000, - "timeout": 5000 - }, - "prf": [...] - }, - "auth": &{ - "scope": [...], - "s": "0xabcdef" - } -} - type SemVer string type Receipt struct { From c066d07f935d2b8a9c4f00174224eed7dba0ad6b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 2 Mar 2023 22:37:11 -0500 Subject: [PATCH 003/127] Input -> Args --- README.md | 60 +++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index a3f1da75..26ee2c8f 100644 --- a/README.md +++ b/README.md @@ -275,7 +275,7 @@ type Await union { # 3 Task -A Task is the smallest unit of work that can be requested from a UCAN. It describes one `(resource, ability, input)` triple. The `input` field is free form, and depend on the specific resource and ability being interacted with, and is not described in this specification. +A Task is the smallest unit of work that can be requested from a UCAN. It describes one `(resource, ability, arguments)` triple. The `args` field is free form, and depend on the specific resource and ability being interacted with, and is not described in this specification. Using the JavaScript analogy from the introduction, a Task is similar to wrapping a call in an anonymous function: @@ -283,7 +283,7 @@ Using the JavaScript analogy from the introduction, a Task is similar to wrappin { "obj" "mailto:alice@example.com", "call": "msg/send", - "input": { + "args": { "to": [ "bob@example.com", "carol@example.com" @@ -315,7 +315,7 @@ Later, when we explore promise [pipelines], this also includes capturing the pro "bafy...sendEmail": { "obj" "mailto://alice@example.com", "call": "msg/send", - "input": { + "args": { "to": { "await/ok": { "/": "bafy...getMailingList" @@ -342,10 +342,10 @@ const sendEmail = msg.send("mailto://alice@example.com", { ```ipldsch type Task struct { - obj URI - call Ability - input {String : Any} - nnc string + obj URI + call Ability + args {String : Any} + nnc string } ``` @@ -359,20 +359,20 @@ The `on` field MUST contain the [URI](https://en.wikipedia.org/wiki/Uniform_Reso The `call` field MUST contain a [UCAN Ability](https://github.com/ucan-wg/spec/#23-ability). This field can be thought of as the message or trait being sent to the resource. -### 3.2.4 Input +### 3.2.4 Arguments -The OPTIONAL `input` field, MAY contain any parameters expected by the URI/Ability pair, which MAY be different between different URIs and Abilities, and is thus left to the executor to define the shape of this data. +The OPTIONAL `args` field, MAY contain any parameters expected by the URI/Ability pair, which MAY be different between different URIs and Abilities, and is thus left to the executor to define the shape of this data. -If present, `input` field MUST have an IPLD [map representation][ipld representation], and thus MAY be a: +If present, `args` MUST have an IPLD [map representation][ipld representation], and thus MUST be one of the following: 1. [struct](https://ipld.io/docs/schemas/features/representation-strategies/#struct-map-representation) in map representation. 2. [keyed](https://ipld.io/docs/schemas/features/representation-strategies/#union-keyed-representation), [enveloped](https://ipld.io/docs/schemas/features/representation-strategies/#union-envelope-representation) or [inline](https://ipld.io/docs/schemas/features/representation-strategies/#union-inline-representation) union. 3. [unit](https://github.com/ipld/ipld/blob/353baf885adebb93191cbe1f7be34f0517e20bbd/specs/schemas/schema-schema.ipldsch#L753-L789) in empty map representation. 4. [map](https://ipld.io/docs/schemas/features/representation-strategies/#map-map-representation) in map representation. -UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type of `input` allowed. +UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type of `args` allowed. -If `input` field is not present, it is implicitly a `unit` represented as empty map. +If the `args` field is not present, it is implicitly a `unit` represented as empty map. ### 3.2.6 Nonce @@ -409,7 +409,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS { "obj" "mailto:akiko@example.com", "call": "msg/send", - "input": { + "args": { "to": [ "boris@example.com", "carol@example.com" @@ -426,7 +426,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS { "obj" "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", "call": "wasm/run", - "input": { + "args": { "func": "add_one", "args": [ 42 @@ -550,7 +550,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt "bafy...createBlogPost": { "obj" "https://example.com/blog/posts", "call": "crud/create", - "input": { + "args": { "headers": { "content-type": "application/json" }, @@ -601,7 +601,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt "bafy...createBlogPostTask": { "obj" "https://example.com/blog/posts", "call": "crud/create", - "input": { + "args": { "headers": { "content-type": "application/json" }, @@ -619,7 +619,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt "bafy...sendEmailTask": { "obj" "mailto:akiko@example.com", "call": "msg/send", - "input": { + "args": { "to": [ "boris@example.com", "carol@example.com" @@ -681,7 +681,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt "bafy...updateDnsTask": { "obj" "dns:example.com?TYPE=TXT", "call": "crud/update", - "input": { + "args": { "value": "hello world" } }, @@ -1018,7 +1018,7 @@ The `s` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without There MAY not be enough information to described an Invocation at creation time. However, all of the information required to construct the next request in a sequence MAY be available in the same Batch, or in a previous (but not yet complete) Invocation. -Some invocations MAY require input from set of other invocations. Waiting for each request to complete before proceeding to the next task has a performance impact due to the amount of latency. [Promise pipelining](http://erights.org/elib/distrib/pipeline.html) is a solution to this problem: by referencing a prior invocation, a pipelined invocation can direct the executor to use the output of one invocations into the input of the other. This liberates the invoker from waiting for each step. +Invocations MAY require arguments from the output of other invocations. Waiting for each request to complete before proceeding to the next task has a performance impact due to the amount of latency. [Promise pipelining](http://erights.org/elib/distrib/pipeline.html) is a solution to this problem: by referencing a prior invocation, a pipelined invocation can direct the executor to use the output of one invocations into the input of the other. This liberates the invoker from waiting for each step. An `Await` MAY be used as a variable placeholder for a concrete value in a [Task] [Invocation] output, waiting on a previous step to complete. @@ -1029,7 +1029,7 @@ For example, consider the following invocation batch: "bafy...createBlogPostTask": { "obj" "https://example.com/blog/posts", "call": "crud/create", - "input": { + "args": { "payload": { "title": "How UCAN Tasks Changed My Life", "body": "This is the story of how one spec changed everything..." @@ -1043,7 +1043,7 @@ For example, consider the following invocation batch: "bafy...sendEmailTask": { "obj" "mailto:akiko@example.com", "call": "msg/send", - "input": { + "args": { "to": { "await/ok": { "/": "bafy...getBlogPostEditorsTask" @@ -1155,7 +1155,7 @@ The [Result] output of the [Task] MAY be reference by wrapping the [Task] in the ## 9.3 Dataflow -Pipelining uses [Await] as inputs to determine the required dataflow graph. The following examples both express the following dataflow graph: +Pipelining uses [Await] as arguments to determine the required dataflow graph. The following examples both express the following dataflow graph: ### 9.3.1 Batched @@ -1182,14 +1182,14 @@ flowchart BR "bafy...updateDnsTask": { "obj" "dns:example.com?TYPE=TXT", "call": "crud/update", - "input": { + "args": { "value": "hello world" } }, "bafy...sendBobEmailTask": { "obj" "mailto://alice@example.com", "call": "msg/send", - "input": { + "args": { "to": "bob@example.com", "subject": "DNSLink for example.com", "body": { @@ -1202,7 +1202,7 @@ flowchart BR "bafy...sendCarolEmailTask": { "obj" "mailto://alice@example.com", "call": "msg/send", - "input": { + "args": { "to": "carol@example.com", "subject": "Hey Carol, DNSLink was updated!", "body": { @@ -1215,7 +1215,7 @@ flowchart BR "bafy...updateReportTask": { "obj" "https://example.com/report", "call": "crud/update", - "input": { + "args": { "payload": { "from": "mailto://alice@exmaple.com", "to": [ @@ -1355,14 +1355,14 @@ flowchart TB "bafy...updateDnsTask": { "obj" "dns:example.com?TYPE=TXT", "call": "crud/update", - "input": { + "args": { "value": "hello world" } }, "bafy...sendBobEmailTask": { "obj" "mailto://alice@example.com", "call": "msg/send", - "input": { + "args": { "to": "bob@example.com", "subject": "DNSLink for example.com", "body": { @@ -1423,7 +1423,7 @@ flowchart TB "bafy...emailCarolTask": { "obj" "mailto://alice@example.com", "call": "msg/send", - "input": { + "args": { "to": "carol@example.com", "subject": "Hey Carol, DNSLink was updated!", "body": { @@ -1436,7 +1436,7 @@ flowchart TB "bafy...updateReportTask": { "obj" "https://example.com/report", "call": "crud/update", - "input": { + "args": { "payload": { "from": "mailto://alice@exmaple.com", "to": [ From dacd00fd86331668ec40d15f32e4a5485993b140 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 3 Mar 2023 17:44:24 -0500 Subject: [PATCH 004/127] Fix typo --- README.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 26ee2c8f..bdc647b8 100644 --- a/README.md +++ b/README.md @@ -281,7 +281,7 @@ Using the JavaScript analogy from the introduction, a Task is similar to wrappin ```json { - "obj" "mailto:alice@example.com", + "obj": "mailto:alice@example.com", "call": "msg/send", "args": { "to": [ @@ -309,11 +309,11 @@ Later, when we explore promise [pipelines], this also includes capturing the pro ```json { "bafy...getMailingList": { - "obj" "https://exmaple.com/mailinglist", + "obj": "https://exmaple.com/mailinglist", "call": "crud/read" }, "bafy...sendEmail": { - "obj" "mailto://alice@example.com", + "obj": "mailto://alice@example.com", "call": "msg/send", "args": { "to": { @@ -384,7 +384,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "obj" "https://example.com/blog/posts", + "obj": "https://example.com/blog/posts", "call": "crud/create", "args": { "headers": { @@ -407,7 +407,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "obj" "mailto:akiko@example.com", + "obj": "mailto:akiko@example.com", "call": "msg/send", "args": { "to": [ @@ -424,7 +424,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "obj" "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", + "obj": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", "call": "wasm/run", "args": { "func": "add_one", @@ -548,7 +548,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt ```json { "bafy...createBlogPost": { - "obj" "https://example.com/blog/posts", + "obj": "https://example.com/blog/posts", "call": "crud/create", "args": { "headers": { @@ -599,7 +599,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt ```json { "bafy...createBlogPostTask": { - "obj" "https://example.com/blog/posts", + "obj": "https://example.com/blog/posts", "call": "crud/create", "args": { "headers": { @@ -617,7 +617,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt } }, "bafy...sendEmailTask": { - "obj" "mailto:akiko@example.com", + "obj": "mailto:akiko@example.com", "call": "msg/send", "args": { "to": [ @@ -679,7 +679,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt ```json { "bafy...updateDnsTask": { - "obj" "dns:example.com?TYPE=TXT", + "obj": "dns:example.com?TYPE=TXT", "call": "crud/update", "args": { "value": "hello world" @@ -1027,7 +1027,7 @@ For example, consider the following invocation batch: ```json { "bafy...createBlogPostTask": { - "obj" "https://example.com/blog/posts", + "obj": "https://example.com/blog/posts", "call": "crud/create", "args": { "payload": { @@ -1037,11 +1037,11 @@ For example, consider the following invocation batch: } }, "bafy...getBlogEditorsTask": { - "obj" "https://example.com/users/editors", + "obj": "https://example.com/users/editors", "call": "crud/read" }, "bafy...sendEmailTask": { - "obj" "mailto:akiko@example.com", + "obj": "mailto:akiko@example.com", "call": "msg/send", "args": { "to": { @@ -1180,14 +1180,14 @@ flowchart BR ```json { "bafy...updateDnsTask": { - "obj" "dns:example.com?TYPE=TXT", + "obj": "dns:example.com?TYPE=TXT", "call": "crud/update", "args": { "value": "hello world" } }, "bafy...sendBobEmailTask": { - "obj" "mailto://alice@example.com", + "obj": "mailto://alice@example.com", "call": "msg/send", "args": { "to": "bob@example.com", @@ -1200,7 +1200,7 @@ flowchart BR } }, "bafy...sendCarolEmailTask": { - "obj" "mailto://alice@example.com", + "obj": "mailto://alice@example.com", "call": "msg/send", "args": { "to": "carol@example.com", @@ -1213,7 +1213,7 @@ flowchart BR } }, "bafy...updateReportTask": { - "obj" "https://example.com/report", + "obj": "https://example.com/report", "call": "crud/update", "args": { "payload": { @@ -1353,14 +1353,14 @@ flowchart TB ```json { "bafy...updateDnsTask": { - "obj" "dns:example.com?TYPE=TXT", + "obj": "dns:example.com?TYPE=TXT", "call": "crud/update", "args": { "value": "hello world" } }, "bafy...sendBobEmailTask": { - "obj" "mailto://alice@example.com", + "obj": "mailto://alice@example.com", "call": "msg/send", "args": { "to": "bob@example.com", @@ -1421,7 +1421,7 @@ flowchart TB ```json { "bafy...emailCarolTask": { - "obj" "mailto://alice@example.com", + "obj": "mailto://alice@example.com", "call": "msg/send", "args": { "to": "carol@example.com", @@ -1434,7 +1434,7 @@ flowchart TB } }, "bafy...updateReportTask": { - "obj" "https://example.com/report", + "obj": "https://example.com/report", "call": "crud/update", "args": { "payload": { From 18c7d9a82ea040a7db156c8459a0daca24f22af2 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 23 Mar 2023 12:16:42 -0700 Subject: [PATCH 005/127] Rename fields --- README.md | 100 +++++++++++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index bdc647b8..f9141e4b 100644 --- a/README.md +++ b/README.md @@ -195,10 +195,10 @@ An [Effect] are the instruction to the [Executor] to run set of [Task]s concurre ```ipldsch type Task struct { - obj URI + uri URI call Ability - args {String : Any} - nnc string + input {String : Any} + nnc string (implicit "") } type URI string @@ -281,9 +281,9 @@ Using the JavaScript analogy from the introduction, a Task is similar to wrappin ```json { - "obj": "mailto:alice@example.com", + "uri" "mailto:alice@example.com", "call": "msg/send", - "args": { + "input": { "to": [ "bob@example.com", "carol@example.com" @@ -309,13 +309,13 @@ Later, when we explore promise [pipelines], this also includes capturing the pro ```json { "bafy...getMailingList": { - "obj": "https://exmaple.com/mailinglist", + "uri" "https://exmaple.com/mailinglist", "call": "crud/read" }, "bafy...sendEmail": { - "obj": "mailto://alice@example.com", + "uri" "mailto://alice@example.com", "call": "msg/send", - "args": { + "input": { "to": { "await/ok": { "/": "bafy...getMailingList" @@ -342,10 +342,10 @@ const sendEmail = msg.send("mailto://alice@example.com", { ```ipldsch type Task struct { - obj URI - call Ability - args {String : Any} - nnc string + uri URI + call Ability + input {String : Any} + nnc string (implicit "") } ``` @@ -370,9 +370,9 @@ If present, `args` MUST have an IPLD [map representation][ipld representation], 3. [unit](https://github.com/ipld/ipld/blob/353baf885adebb93191cbe1f7be34f0517e20bbd/specs/schemas/schema-schema.ipldsch#L753-L789) in empty map representation. 4. [map](https://ipld.io/docs/schemas/features/representation-strategies/#map-map-representation) in map representation. -UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type of `args` allowed. +UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type of `input`s allowed. -If the `args` field is not present, it is implicitly a `unit` represented as empty map. +If the `input` field is not present, it is implicitly a `unit` represented as empty map. ### 3.2.6 Nonce @@ -384,9 +384,9 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "obj": "https://example.com/blog/posts", + "uri" "https://example.com/blog/posts", "call": "crud/create", - "args": { + "input": { "headers": { "content-type": "application/json" }, @@ -407,9 +407,9 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "obj": "mailto:akiko@example.com", + "uri" "mailto:akiko@example.com", "call": "msg/send", - "args": { + "input": { "to": [ "boris@example.com", "carol@example.com" @@ -424,11 +424,11 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "obj": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", + "uri" "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", "call": "wasm/run", - "args": { + "input": { "func": "add_one", - "args": [ + "input": [ 42 ] } @@ -548,9 +548,9 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt ```json { "bafy...createBlogPost": { - "obj": "https://example.com/blog/posts", + "uri" "https://example.com/blog/posts", "call": "crud/create", - "args": { + "input": { "headers": { "content-type": "application/json" }, @@ -599,9 +599,9 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt ```json { "bafy...createBlogPostTask": { - "obj": "https://example.com/blog/posts", + "uri" "https://example.com/blog/posts", "call": "crud/create", - "args": { + "input": { "headers": { "content-type": "application/json" }, @@ -617,9 +617,9 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt } }, "bafy...sendEmailTask": { - "obj": "mailto:akiko@example.com", + "uri" "mailto:akiko@example.com", "call": "msg/send", - "args": { + "input": { "to": [ "boris@example.com", "carol@example.com" @@ -679,9 +679,9 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt ```json { "bafy...updateDnsTask": { - "obj": "dns:example.com?TYPE=TXT", + "uri" "dns:example.com?TYPE=TXT", "call": "crud/update", - "args": { + "input": { "value": "hello world" } }, @@ -1027,9 +1027,9 @@ For example, consider the following invocation batch: ```json { "bafy...createBlogPostTask": { - "obj": "https://example.com/blog/posts", + "uri" "https://example.com/blog/posts", "call": "crud/create", - "args": { + "input": { "payload": { "title": "How UCAN Tasks Changed My Life", "body": "This is the story of how one spec changed everything..." @@ -1037,13 +1037,13 @@ For example, consider the following invocation batch: } }, "bafy...getBlogEditorsTask": { - "obj": "https://example.com/users/editors", + "uri" "https://example.com/users/editors", "call": "crud/read" }, "bafy...sendEmailTask": { - "obj": "mailto:akiko@example.com", + "uri" "mailto:akiko@example.com", "call": "msg/send", - "args": { + "input": { "to": { "await/ok": { "/": "bafy...getBlogPostEditorsTask" @@ -1180,16 +1180,16 @@ flowchart BR ```json { "bafy...updateDnsTask": { - "obj": "dns:example.com?TYPE=TXT", + "uri" "dns:example.com?TYPE=TXT", "call": "crud/update", - "args": { + "input": { "value": "hello world" } }, "bafy...sendBobEmailTask": { - "obj": "mailto://alice@example.com", + "uri" "mailto://alice@example.com", "call": "msg/send", - "args": { + "input": { "to": "bob@example.com", "subject": "DNSLink for example.com", "body": { @@ -1200,9 +1200,9 @@ flowchart BR } }, "bafy...sendCarolEmailTask": { - "obj": "mailto://alice@example.com", + "uri" "mailto://alice@example.com", "call": "msg/send", - "args": { + "input": { "to": "carol@example.com", "subject": "Hey Carol, DNSLink was updated!", "body": { @@ -1213,9 +1213,9 @@ flowchart BR } }, "bafy...updateReportTask": { - "obj": "https://example.com/report", + "uri" "https://example.com/report", "call": "crud/update", - "args": { + "input": { "payload": { "from": "mailto://alice@exmaple.com", "to": [ @@ -1353,16 +1353,16 @@ flowchart TB ```json { "bafy...updateDnsTask": { - "obj": "dns:example.com?TYPE=TXT", + "uri" "dns:example.com?TYPE=TXT", "call": "crud/update", - "args": { + "input": { "value": "hello world" } }, "bafy...sendBobEmailTask": { - "obj": "mailto://alice@example.com", + "uri" "mailto://alice@example.com", "call": "msg/send", - "args": { + "input": { "to": "bob@example.com", "subject": "DNSLink for example.com", "body": { @@ -1421,9 +1421,9 @@ flowchart TB ```json { "bafy...emailCarolTask": { - "obj": "mailto://alice@example.com", + "uri" "mailto://alice@example.com", "call": "msg/send", - "args": { + "input": { "to": "carol@example.com", "subject": "Hey Carol, DNSLink was updated!", "body": { @@ -1434,9 +1434,9 @@ flowchart TB } }, "bafy...updateReportTask": { - "obj": "https://example.com/report", + "uri" "https://example.com/report", "call": "crud/update", - "args": { + "input": { "payload": { "from": "mailto://alice@exmaple.com", "to": [ From 67133628325f4923032ef8eefd4a229b035309dd Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 23 Mar 2023 15:05:04 -0700 Subject: [PATCH 006/127] Remove implicit nnc --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f9141e4b..112699e3 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,7 @@ type Task struct { uri URI call Ability input {String : Any} - nnc string (implicit "") + nnc String } type URI string @@ -345,7 +345,7 @@ type Task struct { uri URI call Ability input {String : Any} - nnc string (implicit "") + nnc String } ``` From f07a92e10cd7ecee589b15b0f064ca9c635be75a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 23 Mar 2023 22:37:56 -0700 Subject: [PATCH 007/127] Update schmea with capsule types etc --- README.md | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 112699e3..fa579e59 100644 --- a/README.md +++ b/README.md @@ -204,39 +204,31 @@ type Task struct { type URI string type Ability string -type Authorization struct { - # Authorization is denoted by the set of links been authorized - scope [&Any] - - # Scope signed by the invoker - s VarSig -} - -type Invocation struct { - v SemVer +type Context struct { run &Task meta {String : Any} prf [&UCAN] - auth &Authorization # Receipt of the invocation that caused this invocation cause optional &Invocation } -type SemVer string +type Invocation struct { + ctx Context + sig Varsig +} -type Receipt struct { - # Invocation this is a receipt for - ran &Invocation +type InvocationCapsule struct { + inv Invocation (rename "ucan/invoke@0.2.0") +} - # Output of the invocation - out Result +type Trace struct { + ran &Invocation # Invocation this is a receipt for - # Effects to be performed - fx Effects - - # All the other metadata - meta {String : Any} + out Result # Output of the invocation + fx Effects # Effects to be enqueued + + meta {String : Any} # All the other metadata # Principal that issued this receipt. If omitted issuer is # inferred from the invocation task audience. @@ -246,9 +238,15 @@ type Receipt struct { # delegation chain from executor to the issuer. This should be # omitted when the executor is the issuer. prf [&UCAN] +} - # Signature from the "iss". - s Varsig +type Receipt struct { + trc Trace + sig Varsig +} + +type ReceiptCapsule struct { + rct &Receipt (rename "ucan/receipt@0.2.0") } type Result union { From 6bde69c5cadc7f562a52a2dafbaa9aefb8eb2d19 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 23 Mar 2023 22:43:49 -0700 Subject: [PATCH 008/127] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fa579e59..850e5bf5 100644 --- a/README.md +++ b/README.md @@ -246,7 +246,7 @@ type Receipt struct { } type ReceiptCapsule struct { - rct &Receipt (rename "ucan/receipt@0.2.0") + rct Receipt (rename "ucan/receipt@0.2.0") } type Result union { From 17ac2427dbcc32722853793928decb2507a88438 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 23 Mar 2023 23:00:51 -0700 Subject: [PATCH 009/127] arg -> input in the last few places --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 850e5bf5..514c3cc3 100644 --- a/README.md +++ b/README.md @@ -273,7 +273,7 @@ type Await union { # 3 Task -A Task is the smallest unit of work that can be requested from a UCAN. It describes one `(resource, ability, arguments)` triple. The `args` field is free form, and depend on the specific resource and ability being interacted with, and is not described in this specification. +A Task is the smallest unit of work that can be requested from a UCAN. It describes one `(resource, ability, input)` triple. The `input` field is free form, and depend on the specific resource and ability being interacted with, and is not described in this specification. Using the JavaScript analogy from the introduction, a Task is similar to wrapping a call in an anonymous function: @@ -357,16 +357,16 @@ The `on` field MUST contain the [URI](https://en.wikipedia.org/wiki/Uniform_Reso The `call` field MUST contain a [UCAN Ability](https://github.com/ucan-wg/spec/#23-ability). This field can be thought of as the message or trait being sent to the resource. -### 3.2.4 Arguments +### 3.2.4 Input -The OPTIONAL `args` field, MAY contain any parameters expected by the URI/Ability pair, which MAY be different between different URIs and Abilities, and is thus left to the executor to define the shape of this data. +The `input` field, MAY contain any parameters expected by the URI/Ability pair, which MAY be different between different URIs and Abilities, and is thus left to the executor to define the shape of this data. -If present, `args` MUST have an IPLD [map representation][ipld representation], and thus MUST be one of the following: +The `input` field MUST have an IPLD [map representation][ipld representation], and thus MUST be one of the following: -1. [struct](https://ipld.io/docs/schemas/features/representation-strategies/#struct-map-representation) in map representation. -2. [keyed](https://ipld.io/docs/schemas/features/representation-strategies/#union-keyed-representation), [enveloped](https://ipld.io/docs/schemas/features/representation-strategies/#union-envelope-representation) or [inline](https://ipld.io/docs/schemas/features/representation-strategies/#union-inline-representation) union. -3. [unit](https://github.com/ipld/ipld/blob/353baf885adebb93191cbe1f7be34f0517e20bbd/specs/schemas/schema-schema.ipldsch#L753-L789) in empty map representation. -4. [map](https://ipld.io/docs/schemas/features/representation-strategies/#map-map-representation) in map representation. +1. [Struct](https://ipld.io/docs/schemas/features/representation-strategies/#struct-map-representation) in map representation. +2. [Keyed](https://ipld.io/docs/schemas/features/representation-strategies/#union-keyed-representation), [enveloped](https://ipld.io/docs/schemas/features/representation-strategies/#union-envelope-representation) or [inline](https://ipld.io/docs/schemas/features/representation-strategies/#union-inline-representation) union. +3. [Unit](https://github.com/ipld/ipld/blob/353baf885adebb93191cbe1f7be34f0517e20bbd/specs/schemas/schema-schema.ipldsch#L753-L789) in empty map representation. +4. [Map](https://ipld.io/docs/schemas/features/representation-strategies/#map-map-representation) in map representation. UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type of `input`s allowed. From 6a614e74c0f0eb89d31927d39bfb1f20f02a6a84 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 23 Mar 2023 23:48:56 -0700 Subject: [PATCH 010/127] Updating to capsules --- README.md | 184 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 109 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 514c3cc3..02cf08d6 100644 --- a/README.md +++ b/README.md @@ -489,59 +489,81 @@ The `auth` field MUST be contain an [Authorization] which signs over the `&Task` Concretely, this means that the `&Task` MUST be present in the associated `auth`'s `scope` field. An `Invocation` where the associated [Authorization] does not include the [Task] in the `scope` MUST be considered invalid. -## 5.1 Schema +## 5.1 Context ```ipldsch -type Invocation struct { - v SemVer - +type Context struct { run &Task + meta {String : Any} + prf [&UCAN] # Receipt of the invocation that caused this invocation cause optional &Invocation +} +``` - # Task authorization. - auth &Authorization +### 5.1.1 Task - meta {String : any} +The `run` field MUST contain a link to the [Task] to be run. - prf [&UCAN] -} +### 5.1.2 Metadata -type SemVer string +The OPTIONAL `meta` field MAY be used to include human-readable descriptions, tags, execution hints, resource limits, and so on. If present, the `meta` field MUST contain a map with string keys. The contents of the map are left undefined to encourage extensible use. + +If `meta` field is not present, it is implicitly a `unit` represented as an empty map. + +### 5.1.3 Proofs + +The `prf` field MUST contain links to any UCANs that provide the authority to perform this task. All of the outermost proofs MUST have `aud` field set to the [Executor]'s DID. All of the outmost proofs MUST have `iss` field set to the [Invoker]'s DID. + +### 5.1.4 Cause + +[Task]s MAY be invoked as an effect caused by a prior [Invocation]. Such [Invocation]s SHOULD have a `cause` field set to the [Receipt] link of the [Invocation] that caused it. The linked [Receipt] MUST have an `Effect` (the `fx` field) containing invoked [Task] in the `run` field. + +## 5.2 (Signed) Invocation + +An `Invocation` is a signed `Context`. + +```ipldsch +type Invocation struct { + ctx Context + sig Varsig +} ``` -## 5.2 Fields +### 5.2.1 Context -### 5.2.1 UCAN Task Version +The `Context` containing the `Task` and any configuration. -The `v` field MUST contain the SemVer-formatted version of the UCAN Invocation Specification that this struct conforms to. +### 5.2.2 Signature -### 5.2.2 Task +The `sig` field MUST contain a [varsig] of the `Context`, signed by the issuer of the proofs. -The `run` field MUST contain a link to the [Task] to be run. +## 5.3 Capsule -### 5.2.3 Cause +An invocation capsule associates the `Invocation` with a versioned schema. This MAY be omitted in contexts where the schema and version are clear from context (for example, when nested in another structure that defines the version) -[Task]s MAY be invoked as an effect caused by a prior [Invocation]. Such [Invocation]s SHOULD have a `cause` field set to the [Receipt] link of the [Invocation] that caused it. The linked [Receipt] MUST have an `Effect` (the `fx` field) containing invoked [Task] in the `run` field. +```ipldsch +type InvocationCapsule struct { + inv Invocation (rename "ucan/invoke@0.2.0") +} +``` -### 5.2.4 Authorization +## 5.4 DAG-JSON Example -The `auth` field MUST contain a link to the [Authorization] that authorizes invoked [Task] in the `run` field. The linked [Authorization] MUST contain `run` in its `scope`. +### 5.4.1 Single Invocation -### 5.2.4 Proofs -The `prf` field MUST contain links to any UCANs that provide the authority to perform this task. All of the outermost proofs MUST have `aud` field set to the [Executor]'s DID. All of the outmost proofs MUST have `iss` field set to the [Invoker]'s DID. -### 5.2.6 Metadata -The OPTIONAL `meta` field MAY be used to include human-readable descriptions, tags, execution hints, resource limits, and so on. If present, the `meta` field MUST contain a map with string keys. The contents of the map are left undefined to encourage extensible use. -If `meta` field is not present, it is implicitly a `unit` represented as an empty map. -## 5.3 DAG-JSON Example +// FIXME + + + + -### 5.3.1 Single Invocation ```json { @@ -592,7 +614,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt } ``` -### 5.3.1 Multiple Invocations +### 5.4.2 Multiple Invocations ```json { @@ -672,7 +694,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt } ``` -### 5.3.3 Causal Invocations +### 5.4.3 Causal Invocations ```json { @@ -842,74 +864,86 @@ A `Receipt` is an attestation of the [Result] and requested [Effect]s by a [Task Receipts MUST use the same version as the invocation that they contain. -## 8.1 Schema +## 8.1 Trace ```ipldsch -type Receipt struct { - ran &Invocation - - # output of the invocation - out Result +type Trace struct { + ran &Invocation # Invocation this is a receipt for - # Effects to be performed - fx Effects + out Result # Output of the invocation + fx Effects # Effects to be enqueued - # All the other metadata - meta {String: any} + meta {String : Any} # All the other metadata - # Principal that issued this receipt. - # If omitted issuer is inferred from - # the invocation task audience. + # Principal that issued this receipt. If omitted issuer is + # inferred from the invocation task audience. iss optional Principal - # When issuer is different from executor - # this MUST hold a UCAN delegation chain - # from executor to the issuer. - # This field SHOULD be omitted executor is an issuer. + # When issuer is different from executor this MUST hold a UCAN + # delegation chain from executor to the issuer. This should be + # omitted when the executor is the issuer. prf [&UCAN] - - # Signature from the `iss`. - s Varsig } ``` -## 8.2 Fields - -### 8.2.1 Ran Invocation +### 8.1.1 Ran Invocation The `ran` field MUST include a link to the [Invocation] that the Receipt is for. -### 8.2.2 Output +### 8.1.2 Output The `out` field MUST contain the value output of the invocation in [Result] format. -### 8.2.3 Effect +### 8.1.3 Effect The OPTIONAL `fx` field, if present MUST be set to the caused [Effect]. The [Executor] SHOULD invoke contained [Task] to progress a workflow execution. If `fx` does not contain OPTIONAL `join` field, it denotes completion of the current execution thread. -### 8.2.4 Metadata Fields +### 8.1.4 Metadata Fields The OPTIONAL metadata field MAY be omitted or used to contain additional data about the receipt. This field MAY be used for tags, commentary, trace information, and so on. -### 8.2.5 Receipt Issuer +### 8.1.5 Receipt Issuer The OPTIONAL `iss` field, if present MUST contain the DID of the [Executor] delegate that signed it. If field is present, delegation from [Executor] MUST be included in the `prf` field. If `iss` field is omitted, Receipt MUST be signed by the [Executor]. -### 8.2.6 Proofs +### 8.1.6 Proofs If OPTIONAL `prf` field is present, MUST contain link to UCAN delegation authorizing Receipt Issuer (`iss`) to carry [Task] execution. -### 8.2.7 Signature +## 8.2 Receipt + +```ipldsch +type Receipt struct { + trc Trace + sig Varsig +} +``` + +### 8.2.1 Trace -The `s` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without `s` field. The signature MUST be generated by the [Executor] or a delegate if OPTIONAL `iss` field is set. +The `trc` field MUST contain the `Trace` of the `Invocation` that the recept is for. -## 8.3 DAG-JSON Examples +### 8.2.2 Signature -### 8.3.1 Issued by Executor +The `sig` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without `s` field. The signature MUST be generated by the [Executor] or a delegate if OPTIONAL `iss` field is set. + +## 8.3 Capsule + +```ipldsch +type ReceiptCapsule struct { + rct Receipt (rename "ucan/receipt@0.2.0") +} +``` + +A receipt capsule associates the `Receipt` with a versioned schema. This MAY be omitted in contexts where the schema and version are clear from context (for example, when nested in another structure that defines the version). + +## 8.4 DAG-JSON Examples + +### 8.4.1 Issued by Executor ```json { @@ -939,7 +973,7 @@ The `s` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without } ``` -### 8.3.2 Issued by Delegate +### 8.4.2 Issued by Delegate ```json { @@ -975,7 +1009,7 @@ The `s` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without } ``` -### 7.3.3 Receipt with effects +### 8.4.3 Receipt with effects ```json { @@ -1535,20 +1569,20 @@ Thanks to [Christine Lemmer-Webber](https://github.com/cwebber) for the many con Thanks to [Rod Vagg](https://github.com/rvagg/) for the clarifications on IPLD Schema implicits and the general IPLD worldview. -[dag-json]: https://ipld.io/docs/codecs/known/dag-json/ -[varsig]: https://github.com/ChainAgnostic/varsig/ -[ucan-ipld]: https://github.com/ucan-wg/ucan-ipld/ -[ucan]: https://github.com/ucan-wg/spec/ +[authorization]: #4-authorization +[await]: #await [dag-cbor]: https://ipld.io/specs/codecs/dag-cbor/spec/ -[ipld representation]: https://ipld.io/docs/schemas/features/representation-strategies/ -[lazy-vs-eager]: #112-lazy-vs-eager-evaluation -[invoker]: #211-invoker +[dag-json]: https://ipld.io/docs/codecs/known/dag-json/ +[effect]: #7-effect [executor]: #212-executor -[task]: #3-task -[authorization]: #4-authorization [invocation]: #5-invocation -[result]: #6-result -[effect]: #7-effect -[receipt]: #8-receipt +[invoker]: #211-invoker +[ipld representation]: https://ipld.io/docs/schemas/features/representation-strategies/ +[lazy-vs-eager]: #112-lazy-vs-eager-evaluation [pipelines]: #9-pipelines -[await]: #await +[receipt]: #8-receipt +[result]: #6-result +[task]: #3-task +[ucan-ipld]: https://github.com/ucan-wg/ucan-ipld/ +[ucan]: https://github.com/ucan-wg/spec/ +[varsig]: https://github.com/ChainAgnostic/varsig/ From cfe5a4edbcddb3aea8ac44ca799e7495e8c7e672 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 24 Mar 2023 00:20:41 -0700 Subject: [PATCH 011/127] Updating examples --- README.md | 84 ++++++++++++++----------------------------------------- 1 file changed, 21 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 02cf08d6..82577788 100644 --- a/README.md +++ b/README.md @@ -435,6 +435,8 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS # 4 Authorization +// FIXME? + An [Authorization] is cryptographically signed data set. It represents an authorization to run [Task]s that are included in `scope` data set. ## 4.1 Schema @@ -442,7 +444,7 @@ An [Authorization] is cryptographically signed data set. It represents an author ```ipldsch type Authorization struct { # Authorization is denoted by the set of links been authorized - scope [&Any] (implicit []) + scope [&Any] # Scope signed by the invoker s VarSig @@ -648,48 +650,19 @@ type InvocationCapsule struct { "subject": "Coffee" } }, - "bafy...multipleAuth": { - "scope": [ - { - "/": "bafy...sendEmailTask" - }, - { - "/": "bafy...createBlogPostTask" - } - ], - "s": { - "/": { - "bytes": "7aEDQMyGqYw2iwP7uIn+Kav5AWe9l5VnL72Gpkzs1Azp+zs6vnixQPa1aCSrok4XwKkhSlFRmRN8YbyohB6iDFl4CQ8" - } - } - }, "bafy...createBlogPostInvocation": { - "v": "0.1.0", - "run": { - "/": "bafy...createBlogPostTask" - }, - "auth": { - "/": "bafy...multipleAuth" + "ctx": { + "run": {"/": "bafy...createBlogPostTask"}, + "prf": [{"/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a"}] }, - "prf": [ - { - "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" - } - ] + "sig": {"/": "bafy...multipleAuth"} }, "bafy...sendEmailInvocation": { - "v": "0.1.0", - "run": { - "/": "bafy...sendEmailTask" - }, - "auth": { - "/": "bafy...multipleAuth" + "ctx": { + "run": {"/": "bafy...sendEmailTask"}, + "prf": [{"/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4"}] }, - "prf": [ - { - "/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4" - } - ] + "sig": {"/": "bafy...multipleAuth"}, } } ``` @@ -1089,33 +1062,18 @@ For example, consider the following invocation batch: } } }, - "bafy...sendEmailInvoctaion": { - "v": "0.1.0", - "run": { - "/": "bafy...sendEmailTask" - }, - "auth": { - "/": "bafy...auth" - }, - "prf": [ - { - "/": "bafy...proofUcanOutsideExample" - } - ] - }, - "bafy...auth": { - "scope": [ - { + "bafy...sendEmailInvocation": { + "ctx": { + "run": { "/": "bafy...sendEmailTask" }, - { - "/": "bafy...getBlogPostEditorsTask" - }, - { - "/": "bafy...createBlogPostTask" - } - ], - "s": { + "prf": [ + { + "/": "bafy...proofUcanOutsideExample" + } + ] + }, + "sig": { "/": { "bytes": "7aEDQDEGkezG7Bcpeknf2UJ7hpqeL1PZodrYYTSwRjqZPf67P4r1lRZvX+6+9gV+wDZUX0DZLMv64n2fPKnjvxrEugE" } From 7989910044b0fc116bcdc06db84a1bec59313af8 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 24 Mar 2023 17:05:16 -0700 Subject: [PATCH 012/127] Update schema --- README.md | 92 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 82577788..c841404c 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Akiko is going away for the weekend. Her good friend Boris is going to borrow he ## 1.1.2 Lazy vs Eager Evaluation -In a referentially transparent setting, the description of a task is equivalent to having done so: a function and its results are interchangeable. [Programming languages with call-by-need semantics](https://en.wikipedia.org/wiki/Haskell) have shown that this can be an elegant programming model, especially for pure functions. However, _when_ something will run can sometimes be unclear. +In a referentially transparent setting, the description of a task is equivalent to having done so: a function and its results are interchangeable. [Programming languages with call-by-need semantics][Haskell] have shown that this can be an elegant programming model, especially for pure functions. However, _when_ something will run can sometimes be unclear. Most languages use eager evaluation. Eager languages must contend directly with the distinction between a reference to a function and a command to run it. For instance, in JavaScript, adding parentheses to a function will run it. Omitting them lets the program pass around a reference to the function without immediately invoking it. @@ -194,9 +194,9 @@ An [Effect] are the instruction to the [Executor] to run set of [Task]s concurre ## 2.3 IPLD Schema ```ipldsch -type Task struct { - uri URI - call Ability +type Instruction struct { + rsc URI + op Ability input {String : Any} nnc String } @@ -204,8 +204,8 @@ type Task struct { type URI string type Ability string -type Context struct { - run &Task +type Task struct { + run &Instruction meta {String : Any} prf [&UCAN] @@ -213,16 +213,21 @@ type Context struct { cause optional &Invocation } -type Invocation struct { - ctx Context - sig Varsig +type Authorization struct { + scope [&Any] # The set of authorized links + s Varsig # Scope signed by the invoker } -type InvocationCapsule struct { - inv Invocation (rename "ucan/invoke@0.2.0") +type Invocation struct { + task &Task + auth &Authorization } -type Trace struct { +type InvocationCapsule union { + | Invocation ucan/invoke@0.2.0" +} representation keyed + +type Outcome struct { ran &Invocation # Invocation this is a receipt for out Result # Output of the invocation @@ -241,7 +246,7 @@ type Trace struct { } type Receipt struct { - trc Trace + ocm &Outcome sig Varsig } @@ -280,7 +285,7 @@ Using the JavaScript analogy from the introduction, a Task is similar to wrappin ```json { "uri" "mailto:alice@example.com", - "call": "msg/send", + "op": "msg/send", "input": { "to": [ "bob@example.com", @@ -308,11 +313,11 @@ Later, when we explore promise [pipelines], this also includes capturing the pro { "bafy...getMailingList": { "uri" "https://exmaple.com/mailinglist", - "call": "crud/read" + "op": "crud/read" }, "bafy...sendEmail": { "uri" "mailto://alice@example.com", - "call": "msg/send", + "op": "msg/send", "input": { "to": { "await/ok": { @@ -341,7 +346,7 @@ const sendEmail = msg.send("mailto://alice@example.com", { ```ipldsch type Task struct { uri URI - call Ability + op Ability input {String : Any} nnc String } @@ -351,11 +356,13 @@ type Task struct { ### 3.2.1 Resource -The `on` field MUST contain the [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) of the resource being accessed. If the resource being accessed is some static data, it is RECOMMENDED to reference it by the [`data`](https://en.wikipedia.org/wiki/Data_URI_scheme), [`ipfs`](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#native-urls), or [`magnet`](https://en.wikipedia.org/wiki/Magnet_URI_scheme) URI schemes. +FIXME + +The `uri` field MUST contain the [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) of the resource being accessed. If the resource being accessed is some static data, it is RECOMMENDED to reference it by the [`data`](https://en.wikipedia.org/wiki/Data_URI_scheme), [`ipfs`](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#native-urls), or [`magnet`](https://en.wikipedia.org/wiki/Magnet_URI_scheme) URI schemes. ### 3.2.3 Ability -The `call` field MUST contain a [UCAN Ability](https://github.com/ucan-wg/spec/#23-ability). This field can be thought of as the message or trait being sent to the resource. +The `op` field MUST contain a [UCAN Ability](https://github.com/ucan-wg/spec/#23-ability). This field can be thought of as the message or trait being sent to the resource. ### 3.2.4 Input @@ -383,7 +390,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { "uri" "https://example.com/blog/posts", - "call": "crud/create", + "op": "crud/create", "input": { "headers": { "content-type": "application/json" @@ -406,7 +413,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { "uri" "mailto:akiko@example.com", - "call": "msg/send", + "op": "msg/send", "input": { "to": [ "boris@example.com", @@ -423,7 +430,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { "uri" "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", - "call": "wasm/run", + "op": "wasm/run", "input": { "func": "add_one", "input": [ @@ -571,7 +578,7 @@ type InvocationCapsule struct { { "bafy...createBlogPost": { "uri" "https://example.com/blog/posts", - "call": "crud/create", + "op": "crud/create", "input": { "headers": { "content-type": "application/json" @@ -622,7 +629,7 @@ type InvocationCapsule struct { { "bafy...createBlogPostTask": { "uri" "https://example.com/blog/posts", - "call": "crud/create", + "op": "crud/create", "input": { "headers": { "content-type": "application/json" @@ -640,7 +647,7 @@ type InvocationCapsule struct { }, "bafy...sendEmailTask": { "uri" "mailto:akiko@example.com", - "call": "msg/send", + "op": "msg/send", "input": { "to": [ "boris@example.com", @@ -673,7 +680,7 @@ type InvocationCapsule struct { { "bafy...updateDnsTask": { "uri" "dns:example.com?TYPE=TXT", - "call": "crud/update", + "op": "crud/update", "input": { "value": "hello world" } @@ -837,10 +844,10 @@ A `Receipt` is an attestation of the [Result] and requested [Effect]s by a [Task Receipts MUST use the same version as the invocation that they contain. -## 8.1 Trace +## 8.1 Response ```ipldsch -type Trace struct { +type Outcome struct { ran &Invocation # Invocation this is a receipt for out Result # Output of the invocation @@ -891,14 +898,14 @@ If OPTIONAL `prf` field is present, MUST contain link to UCAN delegation authori ```ipldsch type Receipt struct { - trc Trace + ocm Outcome sig Varsig } ``` -### 8.2.1 Trace +### 8.2.1 Outcome -The `trc` field MUST contain the `Trace` of the `Invocation` that the recept is for. +The `ocm` field MUST contain the `Outcome` of the `Invocation` that the recept is for. ### 8.2.2 Signature @@ -1033,7 +1040,7 @@ For example, consider the following invocation batch: { "bafy...createBlogPostTask": { "uri" "https://example.com/blog/posts", - "call": "crud/create", + "op": "crud/create", "input": { "payload": { "title": "How UCAN Tasks Changed My Life", @@ -1043,11 +1050,11 @@ For example, consider the following invocation batch: }, "bafy...getBlogEditorsTask": { "uri" "https://example.com/users/editors", - "call": "crud/read" + "op": "crud/read" }, "bafy...sendEmailTask": { "uri" "mailto:akiko@example.com", - "call": "msg/send", + "op": "msg/send", "input": { "to": { "await/ok": { @@ -1171,14 +1178,14 @@ flowchart BR { "bafy...updateDnsTask": { "uri" "dns:example.com?TYPE=TXT", - "call": "crud/update", + "op": "crud/update", "input": { "value": "hello world" } }, "bafy...sendBobEmailTask": { "uri" "mailto://alice@example.com", - "call": "msg/send", + "op": "msg/send", "input": { "to": "bob@example.com", "subject": "DNSLink for example.com", @@ -1191,7 +1198,7 @@ flowchart BR }, "bafy...sendCarolEmailTask": { "uri" "mailto://alice@example.com", - "call": "msg/send", + "op": "msg/send", "input": { "to": "carol@example.com", "subject": "Hey Carol, DNSLink was updated!", @@ -1204,7 +1211,7 @@ flowchart BR }, "bafy...updateReportTask": { "uri" "https://example.com/report", - "call": "crud/update", + "op": "crud/update", "input": { "payload": { "from": "mailto://alice@exmaple.com", @@ -1344,14 +1351,14 @@ flowchart TB { "bafy...updateDnsTask": { "uri" "dns:example.com?TYPE=TXT", - "call": "crud/update", + "op": "crud/update", "input": { "value": "hello world" } }, "bafy...sendBobEmailTask": { "uri" "mailto://alice@example.com", - "call": "msg/send", + "op": "msg/send", "input": { "to": "bob@example.com", "subject": "DNSLink for example.com", @@ -1412,7 +1419,7 @@ flowchart TB { "bafy...emailCarolTask": { "uri" "mailto://alice@example.com", - "call": "msg/send", + "op": "msg/send", "input": { "to": "carol@example.com", "subject": "Hey Carol, DNSLink was updated!", @@ -1425,7 +1432,7 @@ flowchart TB }, "bafy...updateReportTask": { "uri" "https://example.com/report", - "call": "crud/update", + "op": "crud/update", "input": { "payload": { "from": "mailto://alice@exmaple.com", @@ -1544,3 +1551,4 @@ Thanks to [Rod Vagg](https://github.com/rvagg/) for the clarifications on IPLD S [ucan-ipld]: https://github.com/ucan-wg/ucan-ipld/ [ucan]: https://github.com/ucan-wg/spec/ [varsig]: https://github.com/ChainAgnostic/varsig/ +[Haskell]: https://en.wikipedia.org/wiki/Haskell From ebcfaddae012fcb965d91d525f8ce797b2a1422f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 24 Mar 2023 17:05:49 -0700 Subject: [PATCH 013/127] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c841404c..56525961 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,7 @@ type Invocation struct { } type InvocationCapsule union { - | Invocation ucan/invoke@0.2.0" + | Invocation "ucan/invoke@0.2.0" } representation keyed type Outcome struct { From 9bedb28fca2d3e96743ec0832797af9b5daa5e29 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 27 Mar 2023 12:47:07 -0700 Subject: [PATCH 014/127] Update per conversations last week --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 56525961..096019a3 100644 --- a/README.md +++ b/README.md @@ -215,7 +215,7 @@ type Task struct { type Authorization struct { scope [&Any] # The set of authorized links - s Varsig # Scope signed by the invoker + auth Authorization # Scope signed by the invoker } type Invocation struct { @@ -247,7 +247,7 @@ type Outcome struct { type Receipt struct { ocm &Outcome - sig Varsig + auth &Authorization } type ReceiptCapsule struct { @@ -844,7 +844,7 @@ A `Receipt` is an attestation of the [Result] and requested [Effect]s by a [Task Receipts MUST use the same version as the invocation that they contain. -## 8.1 Response +## 8.1 Outcome ```ipldsch type Outcome struct { @@ -905,7 +905,7 @@ type Receipt struct { ### 8.2.1 Outcome -The `ocm` field MUST contain the `Outcome` of the `Invocation` that the recept is for. +The `ocm` field MUST contain the `Outcome` of the `Invocation` that the receipt is for. ### 8.2.2 Signature From 1cb5b44c1dbd4d397a26b113d9fe7a3d0d7cd030 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 28 Mar 2023 13:21:07 -0700 Subject: [PATCH 015/127] Fix schema --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 096019a3..0274c304 100644 --- a/README.md +++ b/README.md @@ -262,17 +262,17 @@ type Result union { # Represents a request to invoke enclosed set of tasks concurrently type Effects struct { # Primary set of tasks to be invoked - fork [&Task] + fork [&Instruction] # Continuation for straight-line programs - join optional &Task + join optional &Instruction } # Way to reference result of the Task type Await union { - | &Task "await/*" - | &Task "await/ok" - | &Task "await/error" + | &Instruction "await/*" + | &Instruction "await/ok" + | &Instruction "await/error" } representation keyed ``` From eb2422bda99f62d341e3f42cb94f07842beb7012 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 28 Mar 2023 14:04:51 -0700 Subject: [PATCH 016/127] "uri" -> "rsc" --- README.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 0274c304..1c928752 100644 --- a/README.md +++ b/README.md @@ -284,7 +284,7 @@ Using the JavaScript analogy from the introduction, a Task is similar to wrappin ```json { - "uri" "mailto:alice@example.com", + "rsc" "mailto:alice@example.com", "op": "msg/send", "input": { "to": [ @@ -312,11 +312,11 @@ Later, when we explore promise [pipelines], this also includes capturing the pro ```json { "bafy...getMailingList": { - "uri" "https://exmaple.com/mailinglist", + "rsc" "https://exmaple.com/mailinglist", "op": "crud/read" }, "bafy...sendEmail": { - "uri" "mailto://alice@example.com", + "rsc" "mailto://alice@example.com", "op": "msg/send", "input": { "to": { @@ -389,7 +389,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "uri" "https://example.com/blog/posts", + "rsc" "https://example.com/blog/posts", "op": "crud/create", "input": { "headers": { @@ -412,7 +412,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "uri" "mailto:akiko@example.com", + "rsc" "mailto:akiko@example.com", "op": "msg/send", "input": { "to": [ @@ -429,7 +429,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "uri" "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", + "rsc" "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", "op": "wasm/run", "input": { "func": "add_one", @@ -577,7 +577,7 @@ type InvocationCapsule struct { ```json { "bafy...createBlogPost": { - "uri" "https://example.com/blog/posts", + "rsc" "https://example.com/blog/posts", "op": "crud/create", "input": { "headers": { @@ -628,7 +628,7 @@ type InvocationCapsule struct { ```json { "bafy...createBlogPostTask": { - "uri" "https://example.com/blog/posts", + "rsc" "https://example.com/blog/posts", "op": "crud/create", "input": { "headers": { @@ -646,7 +646,7 @@ type InvocationCapsule struct { } }, "bafy...sendEmailTask": { - "uri" "mailto:akiko@example.com", + "rsc" "mailto:akiko@example.com", "op": "msg/send", "input": { "to": [ @@ -679,7 +679,7 @@ type InvocationCapsule struct { ```json { "bafy...updateDnsTask": { - "uri" "dns:example.com?TYPE=TXT", + "rsc" "dns:example.com?TYPE=TXT", "op": "crud/update", "input": { "value": "hello world" @@ -1039,7 +1039,7 @@ For example, consider the following invocation batch: ```json { "bafy...createBlogPostTask": { - "uri" "https://example.com/blog/posts", + "rsc" "https://example.com/blog/posts", "op": "crud/create", "input": { "payload": { @@ -1049,11 +1049,11 @@ For example, consider the following invocation batch: } }, "bafy...getBlogEditorsTask": { - "uri" "https://example.com/users/editors", + "rsc" "https://example.com/users/editors", "op": "crud/read" }, "bafy...sendEmailTask": { - "uri" "mailto:akiko@example.com", + "rsc" "mailto:akiko@example.com", "op": "msg/send", "input": { "to": { @@ -1177,14 +1177,14 @@ flowchart BR ```json { "bafy...updateDnsTask": { - "uri" "dns:example.com?TYPE=TXT", + "rsc" "dns:example.com?TYPE=TXT", "op": "crud/update", "input": { "value": "hello world" } }, "bafy...sendBobEmailTask": { - "uri" "mailto://alice@example.com", + "rsc" "mailto://alice@example.com", "op": "msg/send", "input": { "to": "bob@example.com", @@ -1197,7 +1197,7 @@ flowchart BR } }, "bafy...sendCarolEmailTask": { - "uri" "mailto://alice@example.com", + "rsc" "mailto://alice@example.com", "op": "msg/send", "input": { "to": "carol@example.com", @@ -1210,7 +1210,7 @@ flowchart BR } }, "bafy...updateReportTask": { - "uri" "https://example.com/report", + "rsc" "https://example.com/report", "op": "crud/update", "input": { "payload": { @@ -1350,14 +1350,14 @@ flowchart TB ```json { "bafy...updateDnsTask": { - "uri" "dns:example.com?TYPE=TXT", + "rsc" "dns:example.com?TYPE=TXT", "op": "crud/update", "input": { "value": "hello world" } }, "bafy...sendBobEmailTask": { - "uri" "mailto://alice@example.com", + "rsc" "mailto://alice@example.com", "op": "msg/send", "input": { "to": "bob@example.com", @@ -1418,7 +1418,7 @@ flowchart TB ```json { "bafy...emailCarolTask": { - "uri" "mailto://alice@example.com", + "rsc" "mailto://alice@example.com", "op": "msg/send", "input": { "to": "carol@example.com", @@ -1431,7 +1431,7 @@ flowchart TB } }, "bafy...updateReportTask": { - "uri" "https://example.com/report", + "rsc" "https://example.com/report", "op": "crud/update", "input": { "payload": { From 0cbdedb12dc682f0b13051ce943c4eae336a795f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 28 Mar 2023 16:59:15 -0700 Subject: [PATCH 017/127] Update multiple fields --- README.md | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 1c928752..bc722d7c 100644 --- a/README.md +++ b/README.md @@ -209,17 +209,17 @@ type Task struct { meta {String : Any} prf [&UCAN] - # Receipt of the invocation that caused this invocation - cause optional &Invocation + # Receipt of the invocation that caused this Task to be run + cause optional &Instruction } type Authorization struct { scope [&Any] # The set of authorized links - auth Authorization # Scope signed by the invoker + s Varsig # Scope signed by the invoker } type Invocation struct { - task &Task + task Task auth &Authorization } @@ -246,8 +246,8 @@ type Outcome struct { } type Receipt struct { - ocm &Outcome - auth &Authorization + ocm Outcome + sig Varsig } type ReceiptCapsule struct { @@ -358,7 +358,7 @@ type Task struct { FIXME -The `uri` field MUST contain the [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) of the resource being accessed. If the resource being accessed is some static data, it is RECOMMENDED to reference it by the [`data`](https://en.wikipedia.org/wiki/Data_URI_scheme), [`ipfs`](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#native-urls), or [`magnet`](https://en.wikipedia.org/wiki/Magnet_URI_scheme) URI schemes. +The `rsc` field MUST contain the [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) of the resource being accessed. If the resource being accessed is some static data, it is RECOMMENDED to reference it by the [`data`](https://en.wikipedia.org/wiki/Data_URI_scheme), [`ipfs`](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#native-urls), or [`magnet`](https://en.wikipedia.org/wiki/Magnet_URI_scheme) URI schemes. ### 3.2.3 Ability @@ -450,11 +450,8 @@ An [Authorization] is cryptographically signed data set. It represents an author ```ipldsch type Authorization struct { - # Authorization is denoted by the set of links been authorized - scope [&Any] - - # Scope signed by the invoker - s VarSig + sc ope [&Any] # Authorization is denoted by the set of links been authorized + s Varsig # Scope signed by the invoker } ``` @@ -475,18 +472,10 @@ The `s` field MUST contain a [Varsig] of the [CBOR] encoded `scope` field. ```json { "scope": [ - { - "/": "bafyreihtmwju3okftpeuqe3x3ux5e7c2jescakwnoiyv45vnicke4kdxy4" - }, - { - "/": "bafyreieuo63r3y2nuycaq4b3q2xvco3nprlxiwzcfp4cuupgaywat3z6mq" - } + {"/": "bafyreihtmwju3okftpeuqe3x3ux5e7c2jescakwnoiyv45vnicke4kdxy4"}, + {"/": "bafyreieuo63r3y2nuycaq4b3q2xvco3nprlxiwzcfp4cuupgaywat3z6mq"} ], - "s": { - "/": { - "bytes": "7aEDQIJB8XXJ6hWbwu40fN4bq8+Zq8BxyybSWXatMVU3VsL+yzVYpeJqsEBQE5rNtUJefR5rRCNimKNZMJjA9/udZQQ" - } - } + "s": {"/": {"bytes": "7aEDQIJB8XXJ6hWbwu40fN4bq8+Zq8BxyybSWXatMVU3VsL+yzVYpeJqsEBQE5rNtUJefR5rRCNimKNZMJjA9/udZQQ"}} } ``` From 60bebfa2687a9be24d1f2a8c26aeec0231932479 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 28 Mar 2023 17:02:01 -0700 Subject: [PATCH 018/127] Receipt it is! --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bc722d7c..ec97ac20 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ type Task struct { prf [&UCAN] # Receipt of the invocation that caused this Task to be run - cause optional &Instruction + cause optional &Receipt } type Authorization struct { From 1b87de8aa92251e0516e5508ec9c8a74a070db26 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 29 Mar 2023 23:24:41 -0700 Subject: [PATCH 019/127] Update README.md Co-authored-by: Irakli Gozalishvili Signed-off-by: Brooklyn Zelenka --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ec97ac20..5ad839f8 100644 --- a/README.md +++ b/README.md @@ -250,9 +250,9 @@ type Receipt struct { sig Varsig } -type ReceiptCapsule struct { - rct Receipt (rename "ucan/receipt@0.2.0") -} +type ReceiptCapsule union { + | Receipt "ucan/receipt@0.2.0" +} representation keyed type Result union { | any "ok" # Success From 3e802ee5a8ff18bef81e7022e4703e9a5d0fca64 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 29 Mar 2023 23:32:51 -0700 Subject: [PATCH 020/127] Keep updating with latest naming --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 5ad839f8..145957a9 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,7 @@ const sendEmail = msg.send("mailto://alice@example.com", { ```ipldsch type Task struct { - uri URI + rsc URI op Ability input {String : Any} nnc String @@ -487,11 +487,11 @@ The `auth` field MUST be contain an [Authorization] which signs over the `&Task` Concretely, this means that the `&Task` MUST be present in the associated `auth`'s `scope` field. An `Invocation` where the associated [Authorization] does not include the [Task] in the `scope` MUST be considered invalid. -## 5.1 Context +## 5.1 Task -```ipldsch +```Task type Context struct { - run &Task + run &Instruction meta {String : Any} prf [&UCAN] @@ -500,7 +500,7 @@ type Context struct { } ``` -### 5.1.1 Task +### 5.1.1 Instruction The `run` field MUST contain a link to the [Task] to be run. @@ -514,7 +514,7 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt The `prf` field MUST contain links to any UCANs that provide the authority to perform this task. All of the outermost proofs MUST have `aud` field set to the [Executor]'s DID. All of the outmost proofs MUST have `iss` field set to the [Invoker]'s DID. -### 5.1.4 Cause +### 5.1.4 Optional Cause [Task]s MAY be invoked as an effect caused by a prior [Invocation]. Such [Invocation]s SHOULD have a `cause` field set to the [Receipt] link of the [Invocation] that caused it. The linked [Receipt] MUST have an `Effect` (the `fx` field) containing invoked [Task] in the `run` field. @@ -524,27 +524,27 @@ An `Invocation` is a signed `Context`. ```ipldsch type Invocation struct { - ctx Context - sig Varsig + task Task + auth &Authorization } ``` -### 5.2.1 Context +### 5.2.1 Task -The `Context` containing the `Task` and any configuration. +The `Task` containing the `Instruction` and any configuration. -### 5.2.2 Signature +### 5.2.2 Authorization -The `sig` field MUST contain a [varsig] of the `Context`, signed by the issuer of the proofs. +The `auth` field MUST contain a [varsig] signing over an array of CIDs that includes the `Task`'s CID, signed by the issuer of the proofs. ## 5.3 Capsule An invocation capsule associates the `Invocation` with a versioned schema. This MAY be omitted in contexts where the schema and version are clear from context (for example, when nested in another structure that defines the version) ```ipldsch -type InvocationCapsule struct { - inv Invocation (rename "ucan/invoke@0.2.0") -} +type InvocationCapsule union { + | Invocation "ucan/invoke@0.2.0" +} representation keyed ``` ## 5.4 DAG-JSON Example From 871aeae2b4663c80669363611be9346404188233 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 29 Mar 2023 23:45:49 -0700 Subject: [PATCH 021/127] Remove embedded versions in examples --- README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/README.md b/README.md index 145957a9..6b695511 100644 --- a/README.md +++ b/README.md @@ -596,7 +596,6 @@ type InvocationCapsule union { } }, "bafy...invocation": { - "v": "0.1.0", "run": { "/": "bafy...createBlogPost" }, @@ -687,7 +686,6 @@ type InvocationCapsule union { } }, "bafy...updateDnsInvocation": { - "v": "0.1.0", "run": { "/": "bafy...updateDnsTask" }, @@ -1246,7 +1244,6 @@ flowchart BR } }, "bafy...updateDnsInvocation": { - "v": "0.1.0", "run": { "/": "bafy...updateDnsTask" }, @@ -1260,7 +1257,6 @@ flowchart BR ] }, "bafy...sendBobEmailInvocation": { - "v": "0.1.0", "run": { "/": "bafy...sendBobEmailTask" }, @@ -1274,7 +1270,6 @@ flowchart BR ] }, "bafy...sendCarolEmailInvocation": { - "v": "0.1.0", "run": { "/": "bafy...sendCarolEmailTask" }, @@ -1288,7 +1283,6 @@ flowchart BR ] }, "bafy...updateReportInvocation": { - "v": "0.1.0", "run": { "/": "bafy...updateReportTask" }, @@ -1359,7 +1353,6 @@ flowchart TB } }, "bafy...updateDnsInvocation": { - "v": "0.1.0", "run": { "/": "bafy...updateDnsInvocation" }, @@ -1373,7 +1366,6 @@ flowchart TB ] }, "bafy...sendBobEmailInvocation": { - "v": "0.1.0", "run": { "/": "bafy...sendBobEmailTask" }, @@ -1461,7 +1453,6 @@ flowchart TB } }, "bafy...emailCarolInvocation": { - "v": "0.1.0", "run": { "/": "bafy...emailCarolTask" }, @@ -1475,7 +1466,6 @@ flowchart TB ] }, "bafy...updateReportInvocation": { - "v": "0.1.0", "run": { "/": "bafy...updateReporttask" }, From 6a11a31e22107a904ce0f2909bb998571400de6c Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 30 Mar 2023 00:16:16 -0700 Subject: [PATCH 022/127] Continue updating to new format --- README.md | 68 +++++++++++++++++-------------------------------------- 1 file changed, 21 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 6b695511..c743864e 100644 --- a/README.md +++ b/README.md @@ -450,7 +450,7 @@ An [Authorization] is cryptographically signed data set. It represents an author ```ipldsch type Authorization struct { - sc ope [&Any] # Authorization is denoted by the set of links been authorized + scope [&Any] # Authorization is denoted by the set of links been authorized s Varsig # Scope signed by the invoker } ``` @@ -584,29 +584,15 @@ type InvocationCapsule union { } }, "bafy...auth": { - "scope": [ - { - "/": "bafy...createBlogPost" - } - ], - "s": { - "/": { - "bytes": "7aEDQPPhXNvtVb5/T+O40xXU6TSgJZDFnlVaV3GMlaEo/dvxtyaCLm8uUsFK4xzQsQd82QQUYA6fK506XqjghRlucAQ" - } - } + "scope": [{"/": "bafy...createBlogPostTask"}], + "s": {"/": {"bytes": "7aEDQPPhXNvtVb5/T+O40xXU6TSgJZDFnlVaV3GMlaEo/dvxtyaCLm8uUsFK4xzQsQd82QQUYA6fK506XqjghRlucAQ"}} }, "bafy...invocation": { - "run": { - "/": "bafy...createBlogPost" + "task": { + "run": {"/": "bafy...createBlogPost"}, + "prf": [{"/": "bafy...ucanProof"}] }, - "auth": { - "/": "bafy...auth" - }, - "prf": [ - { - "/": "bafy...ucanProof" - } - ] + "auth": {"/": "bafy...auth"} } } ``` @@ -646,16 +632,18 @@ type InvocationCapsule union { } }, "bafy...createBlogPostInvocation": { - "ctx": { + "task": { "run": {"/": "bafy...createBlogPostTask"}, "prf": [{"/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a"}] }, "sig": {"/": "bafy...multipleAuth"} }, "bafy...sendEmailInvocation": { - "ctx": { + "task": { "run": {"/": "bafy...sendEmailTask"}, - "prf": [{"/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4"}] + "prf": [ + {"/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4"} + ] }, "sig": {"/": "bafy...multipleAuth"}, } @@ -675,30 +663,16 @@ type InvocationCapsule union { }, "bafy...auth": { "scope": [ - { - "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" - } + {"/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny"} ], - "s": { - "/": { - "bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0" - } - } + "s": {"/": { "bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}} }, "bafy...updateDnsInvocation": { - "run": { - "/": "bafy...updateDnsTask" - }, - "auth": { - "/": "bafy...auth" - }, - "cause": { - "/": "bafy...somePriorInvocation" - }, + "run": {"/": "bafy...updateDnsTask"}, + "auth": {"/": "bafy...auth"}, + "cause": {"/": "bafy...somePriorInvocation"}, "prf": [ - { - "/": "bafyreieynwqrabzdhgl652ftsk4mlphcj3bxchkj2aw5eb6dc2wxieilau" - } + {"/": "bafyreieynwqrabzdhgl652ftsk4mlphcj3bxchkj2aw5eb6dc2wxieilau"} ] } } @@ -1107,9 +1081,9 @@ An `Await` describes the eventual output of the referenced [Task] invocation. An ```ipldsch type Await union { - | &Task "await/*" - | &Task "await/ok" - | &Task "await/error" + | &Instruction "await/*" + | &Instruction "await/ok" + | &Instruction "await/error" } representation keyed ``` From 5362df38f54bed923d04a4c0d5c2fa2181fee1c0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 31 Mar 2023 20:39:21 -0700 Subject: [PATCH 023/127] Receipt capsule has pointer not inlined --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c743864e..5b42ba8c 100644 --- a/README.md +++ b/README.md @@ -251,7 +251,7 @@ type Receipt struct { } type ReceiptCapsule union { - | Receipt "ucan/receipt@0.2.0" + | &Receipt "ucan/receipt@0.2.0" } representation keyed type Result union { From d218fecde0227adbcffcdf56a6eb397967b8dd25 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 5 Apr 2023 14:47:20 -0700 Subject: [PATCH 024/127] Update prose & bring in line with top-level schema --- README.md | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 5b42ba8c..09bee343 100644 --- a/README.md +++ b/README.md @@ -250,7 +250,7 @@ type Receipt struct { sig Varsig } -type ReceiptCapsule union { +type ReceiptTag union { | &Receipt "ucan/receipt@0.2.0" } representation keyed @@ -276,11 +276,11 @@ type Await union { } representation keyed ``` -# 3 Task +# 3 Instruction -A Task is the smallest unit of work that can be requested from a UCAN. It describes one `(resource, ability, input)` triple. The `input` field is free form, and depend on the specific resource and ability being interacted with, and is not described in this specification. +An Instruction is the smallest unit of work that can be requested from a UCAN. It describes one `(resource, operation, input)` triple. The `input` field is free form, and depend on the specific resource and ability being interacted with, and is not described in this specification. -Using the JavaScript analogy from the introduction, a Task is similar to wrapping a call in an anonymous function: +Using the JavaScript analogy from the introduction, an Instructon is similar to wrapping a call in a closure: ```json { @@ -307,7 +307,7 @@ Using the JavaScript analogy from the introduction, a Task is similar to wrappin }) ``` -Later, when we explore promise [pipelines], this also includes capturing the promise: +Later, when we explore promise [pipelines], this also includes capturing the promise, also with a natural analogy to closures: ```json { @@ -356,8 +356,6 @@ type Task struct { ### 3.2.1 Resource -FIXME - The `rsc` field MUST contain the [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) of the resource being accessed. If the resource being accessed is some static data, it is RECOMMENDED to reference it by the [`data`](https://en.wikipedia.org/wiki/Data_URI_scheme), [`ipfs`](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#native-urls), or [`magnet`](https://en.wikipedia.org/wiki/Magnet_URI_scheme) URI schemes. ### 3.2.3 Ability @@ -442,8 +440,6 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS # 4 Authorization -// FIXME? - An [Authorization] is cryptographically signed data set. It represents an authorization to run [Task]s that are included in `scope` data set. ## 4.1 Schema @@ -479,24 +475,24 @@ The `s` field MUST contain a [Varsig] of the [CBOR] encoded `scope` field. } ``` -# 5 Invocation +# 5 Task As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is an instruction to the [Executor] to perform enclosed [Task]. [Invocation]s are not executable until they have been provided provable authority (in form of UCANs in the `prf` field) and an [Authorization] (in the `auth` field) from the [Invoker]. The `auth` field MUST be contain an [Authorization] which signs over the `&Task` in `run`. -Concretely, this means that the `&Task` MUST be present in the associated `auth`'s `scope` field. An `Invocation` where the associated [Authorization] does not include the [Task] in the `scope` MUST be considered invalid. +Concretely, this means that the `&Task` MUST be present in the associated `auth`'s `scope` field. A `Receipt` where the associated [Authorization] does not include the [Task] in the `scope` MUST be considered invalid. ## 5.1 Task -```Task -type Context struct { +```ipldsch +type Task struct { run &Instruction meta {String : Any} prf [&UCAN] # Receipt of the invocation that caused this invocation - cause optional &Invocation + cause optional &Receipt } ``` @@ -542,7 +538,7 @@ The `auth` field MUST contain a [varsig] signing over an array of CIDs that incl An invocation capsule associates the `Invocation` with a versioned schema. This MAY be omitted in contexts where the schema and version are clear from context (for example, when nested in another structure that defines the version) ```ipldsch -type InvocationCapsule union { +type InvocationTag union { | Invocation "ucan/invoke@0.2.0" } representation keyed ``` @@ -875,7 +871,7 @@ The `sig` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt withou ## 8.3 Capsule ```ipldsch -type ReceiptCapsule struct { +type ReceiptTag struct { rct Receipt (rename "ucan/receipt@0.2.0") } ``` From 2f8a779ee9b823d13de1e1f031271aba032dbb7e Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 5 Apr 2023 15:03:39 -0700 Subject: [PATCH 025/127] update per varsig (remove one level of nesting) --- README.md | 61 +++++++++++++++++++++---------------------------------- 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 09bee343..411928d3 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ - [Brooklyn Zelenka](https://github.com/expede/), [Fission](https://fission.codes/) - [Irakli Gozalishvili](https://github.com/Gozala), [DAG House](https://dag.house/) +- [Zeeshan Lakhani](https://github.com/zeeshanlakhani), [Fission](https://fission.codes/) ## Depends On @@ -223,11 +224,11 @@ type Invocation struct { auth &Authorization } -type InvocationCapsule union { +type InvocationTag union { | Invocation "ucan/invoke@0.2.0" } representation keyed -type Outcome struct { +type Receipt struct { ran &Invocation # Invocation this is a receipt for out Result # Output of the invocation @@ -243,13 +244,10 @@ type Outcome struct { # delegation chain from executor to the issuer. This should be # omitted when the executor is the issuer. prf [&UCAN] -} -type Receipt struct { - ocm Outcome sig Varsig } - + type ReceiptTag union { | &Receipt "ucan/receipt@0.2.0" } representation keyed @@ -533,9 +531,9 @@ The `Task` containing the `Instruction` and any configuration. The `auth` field MUST contain a [varsig] signing over an array of CIDs that includes the `Task`'s CID, signed by the issuer of the proofs. -## 5.3 Capsule +## 5.3 Invocation Tag -An invocation capsule associates the `Invocation` with a versioned schema. This MAY be omitted in contexts where the schema and version are clear from context (for example, when nested in another structure that defines the version) +An invocation capsule associates the `Invocation` with a versioned schema. This field is NOT REQUIRED. Wrapping an `Invocation` in an `InvocationTag` is RECOMMENDED in contexts where the schema and version are not clear from context, such as when it is being stores or passed around without being nested in another structure that defines the version in its schema. ```ipldsch type InvocationTag union { @@ -801,7 +799,7 @@ A `Receipt` is an attestation of the [Result] and requested [Effect]s by a [Task Receipts MUST use the same version as the invocation that they contain. -## 8.1 Outcome +## 8.1 Receipt ```ipldsch type Outcome struct { @@ -820,6 +818,8 @@ type Outcome struct { # delegation chain from executor to the issuer. This should be # omitted when the executor is the issuer. prf [&UCAN] + + sig Varsig } ``` @@ -851,36 +851,23 @@ If `iss` field is omitted, Receipt MUST be signed by the [Executor]. If OPTIONAL `prf` field is present, MUST contain link to UCAN delegation authorizing Receipt Issuer (`iss`) to carry [Task] execution. -## 8.2 Receipt - -```ipldsch -type Receipt struct { - ocm Outcome - sig Varsig -} -``` - -### 8.2.1 Outcome - -The `ocm` field MUST contain the `Outcome` of the `Invocation` that the receipt is for. - -### 8.2.2 Signature +### 8.1.7 Signature The `sig` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without `s` field. The signature MUST be generated by the [Executor] or a delegate if OPTIONAL `iss` field is set. -## 8.3 Capsule +## 8.2 Receipt Tag ```ipldsch -type ReceiptTag struct { - rct Receipt (rename "ucan/receipt@0.2.0") -} +type ReceiptTag union { + | &Receipt "ucan/receipt@0.2.0" +} representation keyed ``` -A receipt capsule associates the `Receipt` with a versioned schema. This MAY be omitted in contexts where the schema and version are clear from context (for example, when nested in another structure that defines the version). +A Receipt Tag associates the `Receipt` with a versioned schema. This MAY be omitted in contexts where the schema and version are clear from context (for example, when nested in another structure that defines the version). -## 8.4 DAG-JSON Examples +## 8.3 DAG-JSON Examples -### 8.4.1 Issued by Executor +### 8.3.1 Issued by Executor ```json { @@ -910,7 +897,7 @@ A receipt capsule associates the `Receipt` with a versioned schema. This MAY be } ``` -### 8.4.2 Issued by Delegate +### 8.3.2 Issued by Delegate ```json { @@ -946,7 +933,7 @@ A receipt capsule associates the `Receipt` with a versioned schema. This MAY be } ``` -### 8.4.3 Receipt with effects +### 8.3.3 Receipt with effects ```json { @@ -1065,7 +1052,7 @@ const notify = msg.send("mailto:akiko@example.com", { }) ``` -Any [Task] field other besides `do` MAY be substituted with `Await`. The `do` field is critical in understanding what kind of action will be performed and CAN NOT be substituted with `Await`. +Any [Task] field other besides `op` MAY be substituted with `Await`. The `op` field is critical in understanding what kind of action will be performed and CAN NOT be substituted with `Await`. An [Await] MAY be used across [Invocation]s with a same [Authorization], or across [Invocation]s with different [Authorization] and MAY even be across multiple Invokers and Executors. As long as the invocation can be resolved, it MAY be promised. This is sometimes referred to as ["promise pipelining"](http://erights.org/elib/distrib/pipeline.html). @@ -1087,11 +1074,11 @@ type Await union { ##### 9.2.1 Success -The successful output of the [Task] MAY be referenced by wrapping the [Task] in the `"await/ok"` tag. +The successful output of the [Instruction] MAY be referenced by wrapping the [Instruction] in the `"await/ok"` tag. -[Executor] MUST fail [Task] that `Await`s successful output of the failed [Task]. +The [Executor] MUST fail an [Instruction] that `Await`s successful output of the failed [Instruction]. -[Executor] MUST substitute [Task] field set to the [Await] of the successful [Task] with an (unwrapped) `ok` value of the output. +Upon learning of a successful execution of an awaited `Instruction`, the [Executor] MUST substitute the [Instruction] field set with the (unwrapped) `ok` value of the output. ##### 9.2.2 Failure @@ -1469,8 +1456,6 @@ Many thanks to [Mark Miller](https://github.com/erights) for his [pioneering wor Many thanks to [Luke Marsen](https://github.com/lukemarsden) and [Simon Worthington](https://github.com/simonwo) for their feedback on invocation model from their work on [Bacalhau](https://www.bacalhau.org/) and [IPVM](https://github.com/ipvm-wg). -Many thanks to [Zeeshan Lakhani](https://github.com/zeeshanlakhani) for his many suggestions, references, clarifications, and suggestions on how to restructure sections for clarity. - Thanks to [Marc-Antoine Parent](https://github.com/maparent) for his discussions of the distinction between declarations and directives both in and out of a UCAN context. Many thanks to [Quinn Wilton](https://github.com/QuinnWilton) for her discussion of speech acts, the dangers of signing canonicalized data, and ergonomics. From ce0f9f8576c7c4773455e4302eb404e194796b83 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 6 Apr 2023 11:27:19 -0700 Subject: [PATCH 026/127] Update examples --- README.md | 71 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 411928d3..f18c1c3c 100644 --- a/README.md +++ b/README.md @@ -559,6 +559,20 @@ type InvocationTag union { ```json { + "_": { + "ucan/invocation@0.2.0": "bafy...invocation" + }, + "bafy...invocation": { + "task": { + "run": {"/": "bafy...createBlogPost"}, + "prf": [{"/": "bafy...ucanProof"}] + }, + "auth": {"/": "bafy...auth"} + }, + "bafy...auth": { + "scope": [{"/": "bafy...createBlogPostTask"}], + "s": {"/": {"bytes": "7aEDQPPhXNvtVb5/T+O40xXU6TSgJZDFnlVaV3GMlaEo/dvxtyaCLm8uUsFK4xzQsQd82QQUYA6fK506XqjghRlucAQ"}} + }, "bafy...createBlogPost": { "rsc" "https://example.com/blog/posts", "op": "crud/create", @@ -576,17 +590,6 @@ type InvocationTag union { "draft": true } } - }, - "bafy...auth": { - "scope": [{"/": "bafy...createBlogPostTask"}], - "s": {"/": {"bytes": "7aEDQPPhXNvtVb5/T+O40xXU6TSgJZDFnlVaV3GMlaEo/dvxtyaCLm8uUsFK4xzQsQd82QQUYA6fK506XqjghRlucAQ"}} - }, - "bafy...invocation": { - "task": { - "run": {"/": "bafy...createBlogPost"}, - "prf": [{"/": "bafy...ucanProof"}] - }, - "auth": {"/": "bafy...auth"} } } ``` @@ -595,7 +598,7 @@ type InvocationTag union { ```json { - "bafy...createBlogPostTask": { + "bafy...createBlogPostInstruction": { "rsc" "https://example.com/blog/posts", "op": "crud/create", "input": { @@ -613,7 +616,7 @@ type InvocationTag union { } } }, - "bafy...sendEmailTask": { + "bafy...sendEmailInstruction": { "rsc" "mailto:akiko@example.com", "op": "msg/send", "input": { @@ -625,14 +628,14 @@ type InvocationTag union { "subject": "Coffee" } }, - "bafy...createBlogPostInvocation": { + "bafy...createBlogPostTask": { "task": { "run": {"/": "bafy...createBlogPostTask"}, "prf": [{"/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a"}] }, "sig": {"/": "bafy...multipleAuth"} }, - "bafy...sendEmailInvocation": { + "bafy...sendEmailTask": { "task": { "run": {"/": "bafy...sendEmailTask"}, "prf": [ @@ -640,7 +643,21 @@ type InvocationTag union { ] }, "sig": {"/": "bafy...multipleAuth"}, - } + }, + "bafy...sendEmailInvocation": { + "task": {"/": "bafy...sendEmailTask"}, + "auth": {"/": "bafy...multipleAuth"} + }, + "bafy...createBlogPostInvocation": { + "task": {"/": "bafy...createBlogPostTask"}, + "auth": {"/": "bafy...multipleAuth"} + }, + "bafy...multipleAuth": { + "scope": [ + {"/": "bafy...createBlogPostTask"}, + {"/": "bafy...sendEmailTask"} + ], + "s": {"/": {"bytes": "7aEDQKxIrga+88HNDd69Ho4Ggz8zkf+GxWC6dAGYua6l85YgiL3NqGxyGAygiSZtWrWUo6SokgOys2wYE7N+novtcwo"}} } ``` @@ -648,27 +665,31 @@ type InvocationTag union { ```json { - "bafy...updateDnsTask": { + "bafy...updateDnsInstruction": { "rsc" "dns:example.com?TYPE=TXT", "op": "crud/update", "input": { "value": "hello world" } }, - "bafy...auth": { - "scope": [ - {"/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny"} - ], - "s": {"/": { "bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}} - }, - "bafy...updateDnsInvocation": { + "bafy...updateDnsTask": { "run": {"/": "bafy...updateDnsTask"}, "auth": {"/": "bafy...auth"}, "cause": {"/": "bafy...somePriorInvocation"}, "prf": [ {"/": "bafyreieynwqrabzdhgl652ftsk4mlphcj3bxchkj2aw5eb6dc2wxieilau"} ] - } + }, + "bafy...updateDnsInvocation": { + "task": {"/": "bafy...updateDnsTask"}, + "auth": {"/": "bafy...auth"} + }, + "bafy...auth": { + "scope": [ + {"/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny"} + ], + "s": {"/": { "bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}} + }, } ``` From 46599e00a4cb6a9f3d7dd22b51e461a34486aa40 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 6 Apr 2023 11:38:17 -0700 Subject: [PATCH 027/127] Fix some internal links --- README.md | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index f18c1c3c..4d00da65 100644 --- a/README.md +++ b/README.md @@ -168,9 +168,13 @@ The executor MUST be the UCAN delegate. Their DID MUST be set the in `aud` field ## 2.2 Components -### 2.2.1 Task +### 2.2.1 Instruction -A [Task] is like a deferred function application: a request to perform some action on a resource with specific input. +An [Instruction] is like a deferred function application: a request to perform some action on a resource with specific input. + +### 2.2.2 Task + +A [Task] wraps an [Instruction] with runtime configuration, including timeouts, fuel, trace metadata, and so on. ### 2.2.2 Authorization @@ -178,11 +182,11 @@ An [Authorization] is a cryptographically signed proof permitting execution of r ### 2.2.3 Invocation -An [Invocation] is a command to the [Executor] to run the [Task], authorized by the [Invoker]. +An [Invocation] is an [Authorized] [Task]. ### 2.2.4 Result -A [Result] is the output of a [Task]. +A [Result] is the output of an [Instruction]. ### 2.2.5 Receipt @@ -1489,21 +1493,22 @@ Thanks to [Christine Lemmer-Webber](https://github.com/cwebber) for the many con Thanks to [Rod Vagg](https://github.com/rvagg/) for the clarifications on IPLD Schema implicits and the general IPLD worldview. -[authorization]: #4-authorization -[await]: #await +[Authorization]: #4-authorization +[Await]: #await +[Effect]: #7-effect +[Executor]: #212-executor +[Haskell]: https://en.wikipedia.org/wiki/Haskell +[Instruction]: #3-instruction +[Invocation]: #5-invocation +[Invoker]: #211-invoker +[Pipeline]: #9-pipelines +[Receipt]: #8-receipt +[Result]: #6-result +[Task]: #5-task +[UCAN]: https://github.com/ucan-wg/spec/ +[Varsig]: https://github.com/ChainAgnostic/varsig/ [dag-cbor]: https://ipld.io/specs/codecs/dag-cbor/spec/ [dag-json]: https://ipld.io/docs/codecs/known/dag-json/ -[effect]: #7-effect -[executor]: #212-executor -[invocation]: #5-invocation -[invoker]: #211-invoker [ipld representation]: https://ipld.io/docs/schemas/features/representation-strategies/ [lazy-vs-eager]: #112-lazy-vs-eager-evaluation -[pipelines]: #9-pipelines -[receipt]: #8-receipt -[result]: #6-result -[task]: #3-task [ucan-ipld]: https://github.com/ucan-wg/ucan-ipld/ -[ucan]: https://github.com/ucan-wg/spec/ -[varsig]: https://github.com/ChainAgnostic/varsig/ -[Haskell]: https://en.wikipedia.org/wiki/Haskell From 02d2ecdf132dad9fee3a9e1845857a7410c0fad1 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 6 Apr 2023 11:39:16 -0700 Subject: [PATCH 028/127] Spelling --- README.md | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 4d00da65..dbdc7b0d 100644 --- a/README.md +++ b/README.md @@ -282,7 +282,7 @@ type Await union { An Instruction is the smallest unit of work that can be requested from a UCAN. It describes one `(resource, operation, input)` triple. The `input` field is free form, and depend on the specific resource and ability being interacted with, and is not described in this specification. -Using the JavaScript analogy from the introduction, an Instructon is similar to wrapping a call in a closure: +Using the JavaScript analogy from the introduction, an Instruction is similar to wrapping a call in a closure: ```json { @@ -533,7 +533,7 @@ The `Task` containing the `Instruction` and any configuration. ### 5.2.2 Authorization -The `auth` field MUST contain a [varsig] signing over an array of CIDs that includes the `Task`'s CID, signed by the issuer of the proofs. +The `auth` field MUST contain a [Varsig] signing over an array of CIDs that includes the `Task`'s CID, signed by the issuer of the proofs. ## 5.3 Invocation Tag @@ -549,18 +549,6 @@ type InvocationTag union { ### 5.4.1 Single Invocation - - - - - -// FIXME - - - - - - ```json { "_": { From 673065aee1edf9947da5ef9a06e65eb3a6d50c6f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 6 Apr 2023 11:41:35 -0700 Subject: [PATCH 029/127] Starting a consistency pass --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index dbdc7b0d..9812c909 100644 --- a/README.md +++ b/README.md @@ -518,7 +518,7 @@ The `prf` field MUST contain links to any UCANs that provide the authority to pe ## 5.2 (Signed) Invocation -An `Invocation` is a signed `Context`. +An [Invocation] is a signed [Task]. ```ipldsch type Invocation struct { @@ -529,15 +529,15 @@ type Invocation struct { ### 5.2.1 Task -The `Task` containing the `Instruction` and any configuration. +The [Task] containing the [Instruction] and any configuration. ### 5.2.2 Authorization -The `auth` field MUST contain a [Varsig] signing over an array of CIDs that includes the `Task`'s CID, signed by the issuer of the proofs. +The `auth` field MUST contain a [Varsig] signing over an array of CIDs that includes the [Task]'s CID, signed by the issuer of the proofs. ## 5.3 Invocation Tag -An invocation capsule associates the `Invocation` with a versioned schema. This field is NOT REQUIRED. Wrapping an `Invocation` in an `InvocationTag` is RECOMMENDED in contexts where the schema and version are not clear from context, such as when it is being stores or passed around without being nested in another structure that defines the version in its schema. +An invocation capsule associates the [Invocation] with a versioned schema. This field is NOT REQUIRED. Wrapping an [Invocation] in an Invocation Tag is RECOMMENDED in contexts where the schema and version are not clear from context, such as when it is being stores or passed around without being nested in another structure that defines the version in its schema. ```ipldsch type InvocationTag union { @@ -687,7 +687,7 @@ type InvocationTag union { # 6 Result -A `Result` records the output of the [Task], as well as its success or failure state. +A Result records the output of the [Task], as well as its success or failure state. ## 6.1 Schema From 4f677807f9a8aba3a45487168cb348e95cdf4de0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 6 Apr 2023 17:28:32 -0700 Subject: [PATCH 030/127] Fix link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9812c909..a7c6d59f 100644 --- a/README.md +++ b/README.md @@ -1487,7 +1487,7 @@ Thanks to [Rod Vagg](https://github.com/rvagg/) for the clarifications on IPLD S [Executor]: #212-executor [Haskell]: https://en.wikipedia.org/wiki/Haskell [Instruction]: #3-instruction -[Invocation]: #5-invocation +[Invocation]: #52-signed-invocation [Invoker]: #211-invoker [Pipeline]: #9-pipelines [Receipt]: #8-receipt From 5390ffbd71126ef9f0afbb12b262bdcba80b8687 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 6 Apr 2023 17:31:07 -0700 Subject: [PATCH 031/127] Fix markdown --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a7c6d59f..08491664 100644 --- a/README.md +++ b/README.md @@ -309,7 +309,7 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to }) ``` -Later, when we explore promise [pipelines], this also includes capturing the promise, also with a natural analogy to closures: +Later, when we explore promise [pipelines][Pipeline], this also includes capturing the promise, also with a natural analogy to closures: ```json { @@ -727,7 +727,7 @@ If no information is available, this field SHOULD be set to `{}`. The result of an [Invocation] MAY include a request for further actions to be performed via "effects". This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. -Effects describe requests for future work to be performed. All [Invocation]s in an [Effect] block MUST be treated as concurrent, unless explicit data dependencies between them exist via promise [Pipelines]. The `fx` block contains two fields: `fork` and `join`. +Effects describe requests for future work to be performed. All [Invocation]s in an [Effect] block MUST be treated as concurrent, unless explicit data dependencies between them exist via promise [Pipeline]s. The `fx` block contains two fields: `fork` and `join`. [Task]s listed in the `fork` field are first-class and only ordered by promises; they otherwise SHOULD be considered independent and equal. As such, atomic guarantees such as failure of one effect implying failure of other effects if left undefined. From d741eb54f8508d90062178a1bcf5e060970287ad Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 6 Apr 2023 17:32:26 -0700 Subject: [PATCH 032/127] TYpo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 08491664..b7daa7fb 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ By distinguishing invocation from delegation, agents are able to understand the Information about the scheduling, order, and pipelining of tasks is orthogonal to the flow of authority. An agent collaborating with the original executor does not need to know that their call is 3 invocations deep; they only need to know that they been asked to perform some task by the latest invoker. -As we shall see in the [discussion of promise pipelining][pipelines], asking an agent to perform a sequence of tasks before you know the exact parameters requires delegating capabilities for all possible steps in the pipeline. Pulling pipelining detail out of the core UCAN spec serves two functions: it keeps the UCAN spec focused on the flow of authority, and makes salient the level of de facto authority that the executor has (since they can claim any value as having returned for any step). +As we shall see in the [discussion of promise pipelining][Pipeline], asking an agent to perform a sequence of tasks before you know the exact parameters requires delegating capabilities for all possible steps in the pipeline. Pulling pipelining detail out of the core UCAN spec serves two functions: it keeps the UCAN spec focused on the flow of authority, and makes salient the level of de facto authority that the executor has (since they can claim any value as having returned for any step). ```txt ────────────────────────────────────────────Time──────────────────────────────────────────────────────► From f94f631a34bbb87a33bf99072a5503f114cdee3a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 15:23:07 -0700 Subject: [PATCH 033/127] Flip to RC1 ahead of removing all traces of IPLD --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7daa7fb..61359d8e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# UCAN Invocation Specification v0.2.0 +# UCAN Invocation Specification v1.0.0-rc.1 ## Editors From 8d8097463469291f9e9643164939fbe5f49cac5b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 15:45:44 -0700 Subject: [PATCH 034/127] Fix all inlined links --- README.md | 188 ++++++++++++++++-------------------------------------- 1 file changed, 56 insertions(+), 132 deletions(-) diff --git a/README.md b/README.md index 61359d8e..e20ba567 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,7 @@ ## Depends On -- [DAG-CBOR] - [UCAN] -- [UCAN-IPLD] -- [Varsig] # 0 Abstract @@ -24,7 +21,7 @@ UCAN Invocation defines a format for expressing the intention to execute delegat ## Language -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119). +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [BCP 14] when, and only when, they appear in all capitals, as shown here. # 1 Introduction @@ -198,85 +195,6 @@ An [Effect] are the instruction to the [Executor] to run set of [Task]s concurre ## 2.3 IPLD Schema -```ipldsch -type Instruction struct { - rsc URI - op Ability - input {String : Any} - nnc String -} - -type URI string -type Ability string - -type Task struct { - run &Instruction - meta {String : Any} - prf [&UCAN] - - # Receipt of the invocation that caused this Task to be run - cause optional &Receipt -} - -type Authorization struct { - scope [&Any] # The set of authorized links - s Varsig # Scope signed by the invoker -} - -type Invocation struct { - task Task - auth &Authorization -} - -type InvocationTag union { - | Invocation "ucan/invoke@0.2.0" -} representation keyed - -type Receipt struct { - ran &Invocation # Invocation this is a receipt for - - out Result # Output of the invocation - fx Effects # Effects to be enqueued - - meta {String : Any} # All the other metadata - - # Principal that issued this receipt. If omitted issuer is - # inferred from the invocation task audience. - iss optional Principal - - # When issuer is different from executor this MUST hold a UCAN - # delegation chain from executor to the issuer. This should be - # omitted when the executor is the issuer. - prf [&UCAN] - - sig Varsig -} - -type ReceiptTag union { - | &Receipt "ucan/receipt@0.2.0" -} representation keyed - -type Result union { - | any "ok" # Success - | any "error" # Error -} representation kinded - -# Represents a request to invoke enclosed set of tasks concurrently -type Effects struct { - # Primary set of tasks to be invoked - fork [&Instruction] - - # Continuation for straight-line programs - join optional &Instruction -} - -# Way to reference result of the Task -type Await union { - | &Instruction "await/*" - | &Instruction "await/ok" - | &Instruction "await/error" -} representation keyed -``` # 3 Instruction @@ -345,7 +263,7 @@ const sendEmail = msg.send("mailto://alice@example.com", { ## 3.1 Schema -```ipldsch +``` type Task struct { rsc URI op Ability @@ -358,11 +276,11 @@ type Task struct { ### 3.2.1 Resource -The `rsc` field MUST contain the [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) of the resource being accessed. If the resource being accessed is some static data, it is RECOMMENDED to reference it by the [`data`](https://en.wikipedia.org/wiki/Data_URI_scheme), [`ipfs`](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#native-urls), or [`magnet`](https://en.wikipedia.org/wiki/Magnet_URI_scheme) URI schemes. +The `rsc` field MUST contain the [URI] of the resource being accessed. If the resource being accessed is some static data, it is RECOMMENDED to reference it by the [`data`], [`ipfs`], or [`magnet`] URI schemes. ### 3.2.3 Ability -The `op` field MUST contain a [UCAN Ability](https://github.com/ucan-wg/spec/#23-ability). This field can be thought of as the message or trait being sent to the resource. +The `op` field MUST contain a [UCAN Ability]. This field can be thought of as the message or trait being sent to the resource. ### 3.2.4 Input @@ -370,10 +288,10 @@ The `input` field, MAY contain any parameters expected by the URI/Ability pair, The `input` field MUST have an IPLD [map representation][ipld representation], and thus MUST be one of the following: -1. [Struct](https://ipld.io/docs/schemas/features/representation-strategies/#struct-map-representation) in map representation. -2. [Keyed](https://ipld.io/docs/schemas/features/representation-strategies/#union-keyed-representation), [enveloped](https://ipld.io/docs/schemas/features/representation-strategies/#union-envelope-representation) or [inline](https://ipld.io/docs/schemas/features/representation-strategies/#union-inline-representation) union. -3. [Unit](https://github.com/ipld/ipld/blob/353baf885adebb93191cbe1f7be34f0517e20bbd/specs/schemas/schema-schema.ipldsch#L753-L789) in empty map representation. -4. [Map](https://ipld.io/docs/schemas/features/representation-strategies/#map-map-representation) in map representation. +1. [Struct] in map representation. +2. [Keyed], [enveloped] or [inline] union. +3. [Unit] in empty map representation. +4. [Map] in map representation. UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type of `input`s allowed. @@ -446,7 +364,7 @@ An [Authorization] is cryptographically signed data set. It represents an author ## 4.1 Schema -```ipldsch +``` type Authorization struct { scope [&Any] # Authorization is denoted by the set of links been authorized s Varsig # Scope signed by the invoker @@ -487,7 +405,7 @@ Concretely, this means that the `&Task` MUST be present in the associated `auth` ## 5.1 Task -```ipldsch +``` type Task struct { run &Instruction meta {String : Any} @@ -520,7 +438,7 @@ The `prf` field MUST contain links to any UCANs that provide the authority to pe An [Invocation] is a signed [Task]. -```ipldsch +``` type Invocation struct { task Task auth &Authorization @@ -539,7 +457,7 @@ The `auth` field MUST contain a [Varsig] signing over an array of CIDs that incl An invocation capsule associates the [Invocation] with a versioned schema. This field is NOT REQUIRED. Wrapping an [Invocation] in an Invocation Tag is RECOMMENDED in contexts where the schema and version are not clear from context, such as when it is being stores or passed around without being nested in another structure that defines the version in its schema. -```ipldsch +``` type InvocationTag union { | Invocation "ucan/invoke@0.2.0" } representation keyed @@ -691,7 +609,7 @@ A Result records the output of the [Task], as well as its success or failure sta ## 6.1 Schema -```ipldsch +``` type Result union { | any "ok" | any "error" @@ -737,7 +655,7 @@ Tasks in the `fork` field MAY be related to the Task in the `join` field if ther ## 7.1 Schema -```ipldsch +``` # Represents a request to invoke enclosed set of tasks concurrently type Effects { # Primary set of tasks to be invoked @@ -814,7 +732,7 @@ Receipts MUST use the same version as the invocation that they contain. ## 8.1 Receipt -```ipldsch +``` type Outcome struct { ran &Invocation # Invocation this is a receipt for @@ -870,7 +788,7 @@ The `sig` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt withou ## 8.2 Receipt Tag -```ipldsch +``` type ReceiptTag union { | &Receipt "ucan/receipt@0.2.0" } representation keyed @@ -983,11 +901,11 @@ A Receipt Tag associates the `Receipt` with a versioned schema. This MAY be omit > Machines grow faster and memories grow larger. But the speed of light is constant and New York is not getting any closer to Tokyo. As hardware continues to improve, the latency barrier between distant machines will increasingly dominate the performance of distributed computation. When distributed computational steps require unnecessary round trips, compositions of these steps can cause unnecessary cascading sequences of round trips > -> — [Mark Miller](https://github.com/erights), [Robust Composition](http://www.erights.org/talks/thesis/markm-thesis.pdf) +> — [Mark Miller], [Robust Composition] There MAY not be enough information to described an Invocation at creation time. However, all of the information required to construct the next request in a sequence MAY be available in the same Batch, or in a previous (but not yet complete) Invocation. -Invocations MAY require arguments from the output of other invocations. Waiting for each request to complete before proceeding to the next task has a performance impact due to the amount of latency. [Promise pipelining](http://erights.org/elib/distrib/pipeline.html) is a solution to this problem: by referencing a prior invocation, a pipelined invocation can direct the executor to use the output of one invocations into the input of the other. This liberates the invoker from waiting for each step. +Invocations MAY require arguments from the output of other invocations. Waiting for each request to complete before proceeding to the next task has a performance impact due to the amount of latency. [Promise pipelining] is a solution to this problem: by referencing a prior invocation, a pipelined invocation can direct the executor to use the output of one invocations into the input of the other. This liberates the invoker from waiting for each step. An `Await` MAY be used as a variable placeholder for a concrete value in a [Task] [Invocation] output, waiting on a previous step to complete. @@ -1067,7 +985,7 @@ const notify = msg.send("mailto:akiko@example.com", { Any [Task] field other besides `op` MAY be substituted with `Await`. The `op` field is critical in understanding what kind of action will be performed and CAN NOT be substituted with `Await`. -An [Await] MAY be used across [Invocation]s with a same [Authorization], or across [Invocation]s with different [Authorization] and MAY even be across multiple Invokers and Executors. As long as the invocation can be resolved, it MAY be promised. This is sometimes referred to as ["promise pipelining"](http://erights.org/elib/distrib/pipeline.html). +An [Await] MAY be used across [Invocation]s with a same [Authorization], or across [Invocation]s with different [Authorization] and MAY even be across multiple Invokers and Executors. As long as the invocation can be resolved, it MAY be promised. This is sometimes referred to as ["promise pipelining"]. ## Await @@ -1075,7 +993,7 @@ An `Await` describes the eventual output of the referenced [Task] invocation. An ### 9.1 Schema -```ipldsch +``` type Await union { | &Instruction "await/*" | &Instruction "await/ok" @@ -1453,50 +1371,56 @@ flowchart TB # 10 Prior Art -[ucanto RPC](https://github.com/web3-storage/ucanto) from DAG House is a production system that uses UCAN as the basis for an RPC layer. +[ucanto RPC] from DAG House is a production system that uses UCAN as the basis for an RPC layer. -The [Capability Transport Protocol (CapTP)](http://erights.org/elib/distrib/captp/index.html) is one of the most influential object-capability systems, and forms the basis for much of the rest of the items on this list. +The [Capability Transport Protocol (CapTP)] is one of the most influential object-capability systems, and forms the basis for much of the rest of the items on this list. -The [Object Capability Network (OCapN)](https://github.com/ocapn/ocapn) protocol extends CapTP with a generalized networking layer. It has implementations from the [Spritely Institute](https://www.spritely.institute/) and [Agoric](https://agoric.com/). At time of writing, it is in the process of being standardized. +The [Object Capability Network (OCapN)] protocol extends CapTP with a generalized networking layer. It has implementations from the [Spritely Institute] and [Agoric]. At time of writing, it is in the process of being standardized. -[Electronic Rights Transfer Protocol (ERTP)](https://docs.agoric.com/guides/ertp/) builds on top of CapTP for blockchain & digital asset use cases. +[Electronic Rights Transfer Protocol (ERTP)] builds on top of CapTP for blockchain & digital asset use cases. -[Cap 'n Proto RPC](https://capnproto.org/) is an influential RPC framework [based on concepts from CapTP](https://capnproto.org/rpc.html#specification). +[Cap 'n Proto RPC] is an influential RPC framework [based on concepts from CapTP]. # 11 Acknowledgements -Many thanks to [Mark Miller](https://github.com/erights) for his [pioneering work](http://erights.org/talks/thesis/markm-thesis.pdf) on [capability systems](http://erights.org/). +Many thanks to [Mark Miller] for his [pioneering work] on [capability systems]. + +Many thanks to [Luke Marsen] and [Simon Worthington] for their feedback on invocation model from their work on [Bacalhau] and [IPVM]. -Many thanks to [Luke Marsen](https://github.com/lukemarsden) and [Simon Worthington](https://github.com/simonwo) for their feedback on invocation model from their work on [Bacalhau](https://www.bacalhau.org/) and [IPVM](https://github.com/ipvm-wg). +Thanks to [Marc-Antoine Parent] for his discussions of the distinction between declarations and directives both in and out of a UCAN context. -Thanks to [Marc-Antoine Parent](https://github.com/maparent) for his discussions of the distinction between declarations and directives both in and out of a UCAN context. +Many thanks to [Quinn Wilton] for her discussion of speech acts, the dangers of signing canonicalized data, and ergonomics. -Many thanks to [Quinn Wilton](https://github.com/QuinnWilton) for her discussion of speech acts, the dangers of signing canonicalized data, and ergonomics. +Thanks to [Blaine Cook] for sharing their experiences with OAuth 1, irreversible design decisions, and advocating for keeping the spec simple-but-evolvable. -Thanks to [Blaine Cook](https://github.com/blaine) for sharing their experiences with OAuth 1, irreversible design decisions, and advocating for keeping the spec simple-but-evolvable. +Thanks to [Philipp Krüger] for the enthusiastic feedback on the overall design and encoding. -Thanks to [Philipp Krüger](https://github.com/matheus23/) for the enthusiastic feedback on the overall design and encoding. +Thanks to [Christine Lemmer-Webber] for the many conversations about capability systems and the programming models that they enable. -Thanks to [Christine Lemmer-Webber](https://github.com/cwebber) for the many conversations about capability systems and the programming models that they enable. + -Thanks to [Rod Vagg](https://github.com/rvagg/) for the clarifications on IPLD Schema implicits and the general IPLD worldview. + -[Authorization]: #4-authorization -[Await]: #await -[Effect]: #7-effect -[Executor]: #212-executor +[BCP 14]: https://www.rfc-editor.org/info/bcp14 +[Bacalhau]: https://www.bacalhau.org/ +[Brooklyn Zelenka]: https://github.com/expede/ +[DAG House]: https://dag.house +[Fission]: https://fission.codes/ [Haskell]: https://en.wikipedia.org/wiki/Haskell -[Instruction]: #3-instruction -[Invocation]: #52-signed-invocation -[Invoker]: #211-invoker -[Pipeline]: #9-pipelines -[Receipt]: #8-receipt -[Result]: #6-result -[Task]: #5-task +[IPVM]: https://github.com/ipvm-wg +[Irakli Gozalishvili]: https://github.com/Gozala +[Luke Marsen]: https://github.com/lukemarsden +[Mark Miller]: https://github.com/erights +[Philipp Krüger]: https://github.com/matheus23/ +[Robust Composition]: http://www.erights.org/talks/thesis/markm-thesis.pdf +[Simon Worthington]: https://github.com/simonwo +[UCAN Ability]: https://github.com/ucan-wg/spec/#23-ability [UCAN]: https://github.com/ucan-wg/spec/ -[Varsig]: https://github.com/ChainAgnostic/varsig/ -[dag-cbor]: https://ipld.io/specs/codecs/dag-cbor/spec/ -[dag-json]: https://ipld.io/docs/codecs/known/dag-json/ -[ipld representation]: https://ipld.io/docs/schemas/features/representation-strategies/ -[lazy-vs-eager]: #112-lazy-vs-eager-evaluation -[ucan-ipld]: https://github.com/ucan-wg/ucan-ipld/ +[URI]: https://en.wikipedia.org/wiki/Uniform_Resource_Identifier +[Zeeshan Lakhani]: https://github.com/zeeshanlakhani +[`data`]: https://en.wikipedia.org/wiki/Data_URI_scheme +[`ipfs`]: https://docs.ipfs.tech/how-to/address-ipfs-on-web/#native-urls +[`magnet`]: https://en.wikipedia.org/wiki/Magnet_URI_scheme +[eRights]: https:/erights.org +[promise pipelining]: http://erights.org/elib/distrib/pipeline.html +[ucanto RPC]: https://github.com/web3-storage/ucanto From 8972f62528a9eb8913328cf20d5de5f09bbbdd17 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 3 Oct 2023 13:33:48 -0700 Subject: [PATCH 035/127] Switch diagram to mermaid now that GH has it --- README.md | 175 ++++++++++++++++++++++-------------------------------- 1 file changed, 71 insertions(+), 104 deletions(-) diff --git a/README.md b/README.md index e20ba567..b871e575 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,18 @@ ## Editors -- [Brooklyn Zelenka](https://github.com/expede/), [Fission](https://fission.codes/) -- [Irakli Gozalishvili](https://github.com/Gozala), [DAG House](https://dag.house/) +- [Brooklyn Zelenka], [Fission] +- [Irakli Gozalishvili], [DAG House] ## Authors -- [Brooklyn Zelenka](https://github.com/expede/), [Fission](https://fission.codes/) -- [Irakli Gozalishvili](https://github.com/Gozala), [DAG House](https://dag.house/) -- [Zeeshan Lakhani](https://github.com/zeeshanlakhani), [Fission](https://fission.codes/) +- [Brooklyn Zelenka], [Fission] +- [Irakli Gozalishvili], [DAG House] +- [Zeeshan Lakhani], [Fission] ## Depends On -- [UCAN] +- [UCAN Delegation] # 0 Abstract @@ -75,71 +75,31 @@ Information about the scheduling, order, and pipelining of tasks is orthogonal t As we shall see in the [discussion of promise pipelining][Pipeline], asking an agent to perform a sequence of tasks before you know the exact parameters requires delegating capabilities for all possible steps in the pipeline. Pulling pipelining detail out of the core UCAN spec serves two functions: it keeps the UCAN spec focused on the flow of authority, and makes salient the level of de facto authority that the executor has (since they can claim any value as having returned for any step). -```txt - ────────────────────────────────────────────Time──────────────────────────────────────────────────────► - -┌──────────────────────────────────────────Delegation─────────────────────────────────────────────────────┐ -│ │ -│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ -│ │ │ │ │ │ │ │ │ │ │ │ -│ │ Alice ├──►│ Bob ├──►│ Carol ├────────►│ Dan ├───────────────►│ Erin │ │ -│ │ │ │ │ │ │ │ │ │ │ │ -│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────────────────────────────┘ - -┌──────────────────────────────────────────Invocation─────────────────────────────────────────────────────┐ -│ │ -│ ┌─────────┐ ┌─────────┐ │ -│ │ │ │ │ │ -│ │ Carol ╞═══All══►│ Dan │ │ -│ │ │ │ │ │ -│ └─────────┘ └─────────┘ │ -│ │ -│ ┌─────────┐ ┌─────────┐ │ -│ │ │ │ │ │ -│ │ Dan ╞═══════════Update DB═════════►│ Erin │ │ -│ │ │ │ │ │ -│ └─────────┘ └─────────┘ │ -│ │ -│ ┌─────────┐ ┌─────────┐ │ -│ │ │ │ │ │ -│ │ Dan ╞═══Read Email══►│ Erin │ │ -│ │ │ ▲ │ │ │ -│ └─────────┘ ┆ └─────────┘ │ -│ With │ -│ Result │ -│ ┌─────────┐ Of ┌─────────┐ │ -│ │ │ ┆ │ │ │ -│ │ Dan ╞════Set DNS══►│ Erin │ │ -│ │ │ │ │ │ -│ └─────────┘ └─────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +``` mermaid +sequenceDiagram + participant Alice 💾 + participant Bob + participant Carol 📧 + participant Dan + participant Erin + + Note over Alice 💾, Erin: Delegations + Alice 💾 -->> Bob: Delegate + Bob -->> Carol 📧: Delegate + Carol 📧 -->> Dan: Delegate + Carol 📧 -->> Dan: Delegate + Dan -->> Erin: Delegate + + Note over Alice 💾, Erin: Single Invocation + Erin -) Alice 💾: Read from Alice's DB! + Alice 💾 --) Erin: Return + + Note over Alice 💾, Erin: Multiple Invocation Flow + Dan -) Alice 💾: Read from Alice's DB! + Alice 💾 --) Dan: Return + Dan -) Carol 📧: Send email containing as Carol! ``` -## 1.3 A Note On Serialization - -The JSON examples below are given in [DAG-JSON], but UCAN Task is actually defined as IPLD. This makes UCAN Task agnostic to encoding. DAG-JSON follows particular conventions around wrapping CIDs and binary data in tags like so: - -### CID - -```json -{"/": "Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD"} -``` - -### Bytes - -```json -{"/": {"bytes": "s0m3Byte5"}} -``` - -This format help disambiguate type information in generic [DAG-JSON] tooling. However, your presentation need not be in this specific format, as long as it can be converted to and from this cleanly. As it is used for the signature format, [DAG-CBOR] is RECOMMENDED. - -## 1.4 Signatures - -All payloads described in this spec MUST be signed with a [Varsig]. - # 2 High-Level Concepts ## 2.1 Roles @@ -191,11 +151,18 @@ A [Receipt] is a cryptographically signed description of the [Invocation] output ### 2.2.6 Effect -An [Effect] are the instruction to the [Executor] to run set of [Task]s concurrently. +An [Effect] is the instruction to the [Executor] to enqueue a new set of [Task]s. ## 2.3 IPLD Schema + + + + + + + # 3 Instruction An Instruction is the smallest unit of work that can be requested from a UCAN. It describes one `(resource, operation, input)` triple. The `input` field is free form, and depend on the specific resource and ability being interacted with, and is not described in this specification. @@ -204,7 +171,7 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to ```json { - "rsc" "mailto:alice@example.com", + "uri" "mailto:alice@example.com", "op": "msg/send", "input": { "to": [ @@ -232,11 +199,11 @@ Later, when we explore promise [pipelines][Pipeline], this also includes capturi ```json { "bafy...getMailingList": { - "rsc" "https://exmaple.com/mailinglist", + "uri" "https://exmaple.com/mailinglist", "op": "crud/read" }, "bafy...sendEmail": { - "rsc" "mailto://alice@example.com", + "uri" "mailto://alice@example.com", "op": "msg/send", "input": { "to": { @@ -265,7 +232,7 @@ const sendEmail = msg.send("mailto://alice@example.com", { ``` type Task struct { - rsc URI + uri URI op Ability input {String : Any} nnc String @@ -276,17 +243,17 @@ type Task struct { ### 3.2.1 Resource -The `rsc` field MUST contain the [URI] of the resource being accessed. If the resource being accessed is some static data, it is RECOMMENDED to reference it by the [`data`], [`ipfs`], or [`magnet`] URI schemes. +The Resource `(uri`) field MUST contain the [URI] of the resource being accessed. If the resource being accessed is some static data, it is RECOMMENDED to reference it by the [`data`], [`ipfs`], or [`magnet`] URI schemes. ### 3.2.3 Ability -The `op` field MUST contain a [UCAN Ability]. This field can be thought of as the message or trait being sent to the resource. +The Ability (sometimes called "operation") field MUST contain a [UCAN Ability]. This field can be thought of as the message or trait being sent to the resource. ### 3.2.4 Input -The `input` field, MAY contain any parameters expected by the URI/Ability pair, which MAY be different between different URIs and Abilities, and is thus left to the executor to define the shape of this data. +The Input field, MAY contain any parameters expected by the URI/Ability pair, which MAY be different between different URIs and Abilities, and is thus left to the executor to define the shape of this data. -The `input` field MUST have an IPLD [map representation][ipld representation], and thus MUST be one of the following: +The Input field MUST have an IPLD [map representation][ipld representation], and thus MUST be one of the following: 1. [Struct] in map representation. 2. [Keyed], [enveloped] or [inline] union. @@ -307,7 +274,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "rsc" "https://example.com/blog/posts", + "uri" "https://example.com/blog/posts", "op": "crud/create", "input": { "headers": { @@ -330,7 +297,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "rsc" "mailto:akiko@example.com", + "uri" "mailto:akiko@example.com", "op": "msg/send", "input": { "to": [ @@ -347,7 +314,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "rsc" "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", + "uri" "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", "op": "wasm/run", "input": { "func": "add_one", @@ -381,7 +348,7 @@ If the `scope` field is omitted, it is implicitly treated as an empty list (auth ### 4.2.2 Signature -The `s` field MUST contain a [Varsig] of the [CBOR] encoded `scope` field. +The `s` field MUST contain a signature FIXME `scope` field. ## 4.3 DAG-JSON Example @@ -391,7 +358,7 @@ The `s` field MUST contain a [Varsig] of the [CBOR] encoded `scope` field. {"/": "bafyreihtmwju3okftpeuqe3x3ux5e7c2jescakwnoiyv45vnicke4kdxy4"}, {"/": "bafyreieuo63r3y2nuycaq4b3q2xvco3nprlxiwzcfp4cuupgaywat3z6mq"} ], - "s": {"/": {"bytes": "7aEDQIJB8XXJ6hWbwu40fN4bq8+Zq8BxyybSWXatMVU3VsL+yzVYpeJqsEBQE5rNtUJefR5rRCNimKNZMJjA9/udZQQ"}} + "sig": {"/": {"bytes": "7aEDQIJB8XXJ6hWbwu40fN4bq8+Zq8BxyybSWXatMVU3VsL+yzVYpeJqsEBQE5rNtUJefR5rRCNimKNZMJjA9/udZQQ"}} } ``` @@ -484,7 +451,7 @@ type InvocationTag union { "s": {"/": {"bytes": "7aEDQPPhXNvtVb5/T+O40xXU6TSgJZDFnlVaV3GMlaEo/dvxtyaCLm8uUsFK4xzQsQd82QQUYA6fK506XqjghRlucAQ"}} }, "bafy...createBlogPost": { - "rsc" "https://example.com/blog/posts", + "uri" "https://example.com/blog/posts", "op": "crud/create", "input": { "headers": { @@ -509,7 +476,7 @@ type InvocationTag union { ```json { "bafy...createBlogPostInstruction": { - "rsc" "https://example.com/blog/posts", + "uri" "https://example.com/blog/posts", "op": "crud/create", "input": { "headers": { @@ -527,7 +494,7 @@ type InvocationTag union { } }, "bafy...sendEmailInstruction": { - "rsc" "mailto:akiko@example.com", + "uri" "mailto:akiko@example.com", "op": "msg/send", "input": { "to": [ @@ -576,7 +543,7 @@ type InvocationTag union { ```json { "bafy...updateDnsInstruction": { - "rsc" "dns:example.com?TYPE=TXT", + "uri" "dns:example.com?TYPE=TXT", "op": "crud/update", "input": { "value": "hello world" @@ -901,20 +868,20 @@ A Receipt Tag associates the `Receipt` with a versioned schema. This MAY be omit > Machines grow faster and memories grow larger. But the speed of light is constant and New York is not getting any closer to Tokyo. As hardware continues to improve, the latency barrier between distant machines will increasingly dominate the performance of distributed computation. When distributed computational steps require unnecessary round trips, compositions of these steps can cause unnecessary cascading sequences of round trips > -> — [Mark Miller], [Robust Composition] +> — [Mark Miller], [Robust Composition] There MAY not be enough information to described an Invocation at creation time. However, all of the information required to construct the next request in a sequence MAY be available in the same Batch, or in a previous (but not yet complete) Invocation. Invocations MAY require arguments from the output of other invocations. Waiting for each request to complete before proceeding to the next task has a performance impact due to the amount of latency. [Promise pipelining] is a solution to this problem: by referencing a prior invocation, a pipelined invocation can direct the executor to use the output of one invocations into the input of the other. This liberates the invoker from waiting for each step. -An `Await` MAY be used as a variable placeholder for a concrete value in a [Task] [Invocation] output, waiting on a previous step to complete. +A `Promise` MAY be used as a variable placeholder for a concrete value in a [Task] [Invocation] output, waiting on a previous step to complete. For example, consider the following invocation batch: ```json { "bafy...createBlogPostTask": { - "rsc" "https://example.com/blog/posts", + "uri" "https://example.com/blog/posts", "op": "crud/create", "input": { "payload": { @@ -924,11 +891,11 @@ For example, consider the following invocation batch: } }, "bafy...getBlogEditorsTask": { - "rsc" "https://example.com/users/editors", + "uri" "https://example.com/users/editors", "op": "crud/read" }, "bafy...sendEmailTask": { - "rsc" "mailto:akiko@example.com", + "uri" "mailto:akiko@example.com", "op": "msg/send", "input": { "to": { @@ -994,7 +961,7 @@ An `Await` describes the eventual output of the referenced [Task] invocation. An ### 9.1 Schema ``` -type Await union { +type Promise union { | &Instruction "await/*" | &Instruction "await/ok" | &Instruction "await/error" @@ -1052,14 +1019,14 @@ flowchart BR ```json { "bafy...updateDnsTask": { - "rsc" "dns:example.com?TYPE=TXT", + "uri" "dns:example.com?TYPE=TXT", "op": "crud/update", "input": { "value": "hello world" } }, "bafy...sendBobEmailTask": { - "rsc" "mailto://alice@example.com", + "uri" "mailto://alice@example.com", "op": "msg/send", "input": { "to": "bob@example.com", @@ -1072,7 +1039,7 @@ flowchart BR } }, "bafy...sendCarolEmailTask": { - "rsc" "mailto://alice@example.com", + "uri" "mailto://alice@example.com", "op": "msg/send", "input": { "to": "carol@example.com", @@ -1085,7 +1052,7 @@ flowchart BR } }, "bafy...updateReportTask": { - "rsc" "https://example.com/report", + "uri" "https://example.com/report", "op": "crud/update", "input": { "payload": { @@ -1221,14 +1188,14 @@ flowchart TB ```json { "bafy...updateDnsTask": { - "rsc" "dns:example.com?TYPE=TXT", + "uri" "dns:example.com?TYPE=TXT", "op": "crud/update", "input": { "value": "hello world" } }, "bafy...sendBobEmailTask": { - "rsc" "mailto://alice@example.com", + "uri" "mailto://alice@example.com", "op": "msg/send", "input": { "to": "bob@example.com", @@ -1287,7 +1254,7 @@ flowchart TB ```json { "bafy...emailCarolTask": { - "rsc" "mailto://alice@example.com", + "uri" "mailto://alice@example.com", "op": "msg/send", "input": { "to": "carol@example.com", @@ -1300,7 +1267,7 @@ flowchart TB } }, "bafy...updateReportTask": { - "rsc" "https://example.com/report", + "uri" "https://example.com/report", "op": "crud/update", "input": { "payload": { @@ -1371,7 +1338,7 @@ flowchart TB # 10 Prior Art -[ucanto RPC] from DAG House is a production system that uses UCAN as the basis for an RPC layer. +[ucanto RPC] from [DAG House] is a production system that uses UCAN as the basis for an RPC layer. The [Capability Transport Protocol (CapTP)] is one of the most influential object-capability systems, and forms the basis for much of the rest of the items on this list. @@ -1414,8 +1381,8 @@ Thanks to [Christine Lemmer-Webber] for the many conversations about capability [Philipp Krüger]: https://github.com/matheus23/ [Robust Composition]: http://www.erights.org/talks/thesis/markm-thesis.pdf [Simon Worthington]: https://github.com/simonwo -[UCAN Ability]: https://github.com/ucan-wg/spec/#23-ability -[UCAN]: https://github.com/ucan-wg/spec/ +[UCAN Ability]: https://github.com/ucan-wg/delegation/#23-ability +[UCAN Delegation]: https://github.com/ucan-wg/delegation/ [URI]: https://en.wikipedia.org/wiki/Uniform_Resource_Identifier [Zeeshan Lakhani]: https://github.com/zeeshanlakhani [`data`]: https://en.wikipedia.org/wiki/Data_URI_scheme From dbe1c0795ed970ddd7f7d57f0a6f5c96472f0fb2 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 3 Oct 2023 13:49:14 -0700 Subject: [PATCH 036/127] Add pipeline --- README.md | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b871e575..872fac42 100644 --- a/README.md +++ b/README.md @@ -84,20 +84,26 @@ sequenceDiagram participant Erin Note over Alice 💾, Erin: Delegations - Alice 💾 -->> Bob: Delegate - Bob -->> Carol 📧: Delegate - Carol 📧 -->> Dan: Delegate - Carol 📧 -->> Dan: Delegate - Dan -->> Erin: Delegate + Alice 💾 -->> Bob: Delegate + Bob -->> Carol 📧: Delegate + Carol 📧 -->> Dan: Delegate + Carol 📧 -->> Dan: Delegate + Dan -->> Erin: Delegate Note over Alice 💾, Erin: Single Invocation - Erin -) Alice 💾: Read from Alice's DB! - Alice 💾 --) Erin: Return + Erin -) Alice 💾: Read from Alice's DB! + Alice 💾 --) Erin: Return Note over Alice 💾, Erin: Multiple Invocation Flow - Dan -) Alice 💾: Read from Alice's DB! - Alice 💾 --) Dan: Return - Dan -) Carol 📧: Send email containing as Carol! + Dan -) Alice 💾: Read from Alice's DB! + Alice 💾 --) Dan: Return + Dan -) Carol 📧: Send email containing as Carol! + + Note over Alice 💾, Erin: Promise Pipeline + Dan -) Alice 💾: Read from Alice's DB! [Invocation ID = 123] + Dan -) Carol 📧: Send email containing [Inviocation ID = 123] as Carol! + Alice 💾 --) Carol 📧: Return + Carol 📧 -) Carol 📧: Send email! ``` # 2 High-Level Concepts From ebfdb4784a9fb78fd83e71e29e60bd37edaa0bee Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 3 Oct 2023 15:26:37 -0700 Subject: [PATCH 037/127] Add proxy execution --- README.md | 86 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 872fac42..46900ebb 100644 --- a/README.md +++ b/README.md @@ -88,24 +88,40 @@ sequenceDiagram Bob -->> Carol 📧: Delegate Carol 📧 -->> Dan: Delegate Carol 📧 -->> Dan: Delegate - Dan -->> Erin: Delegate Note over Alice 💾, Erin: Single Invocation - Erin -) Alice 💾: Read from Alice's DB! - Alice 💾 --) Erin: Return + Dan -) Alice 💾: Read from Alice's DB! + Alice 💾 --) Dan: Value + + Note over Alice 💾, Erin: Proxy Execution + Dan -->> Erin: Delegate + Dan -) Erin: Run! + Erin -) Alice 💾: Read from Alice's DB! + Alice 💾 --) Erin: Value + Erin -) Dan: Value Note over Alice 💾, Erin: Multiple Invocation Flow Dan -) Alice 💾: Read from Alice's DB! - Alice 💾 --) Dan: Return + Alice 💾 --) Dan: Value Dan -) Carol 📧: Send email containing as Carol! + Carol 📧 -) Carol 📧: Send email! Note over Alice 💾, Erin: Promise Pipeline Dan -) Alice 💾: Read from Alice's DB! [Invocation ID = 123] - Dan -) Carol 📧: Send email containing [Inviocation ID = 123] as Carol! - Alice 💾 --) Carol 📧: Return - Carol 📧 -) Carol 📧: Send email! + Dan -) Carol 📧: Send email containing Result as Carol! + Alice 💾 --) Carol 📧: Value + Carol 📧 -) Carol 📧: Send email containing as Carol! + ``` +## 1.4 Serialization + +Unlike [UCAN Delegation]s, Invocations are point-to-point. This means that — aside from an Instruction ID — the exact format need only be agreed on by the two parties directly communicating. The examples in this document are given as [DAG-JSON] (consistent with [`invocation-ipld`]), but any serialization format MAY be used as long as it's accepted by both parties. + +## 1.5 Public Resources + +A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible and + # 2 High-Level Concepts ## 2.1 Roles @@ -159,16 +175,6 @@ A [Receipt] is a cryptographically signed description of the [Invocation] output An [Effect] is the instruction to the [Executor] to enqueue a new set of [Task]s. -## 2.3 IPLD Schema - - - - - - - - - # 3 Instruction An Instruction is the smallest unit of work that can be requested from a UCAN. It describes one `(resource, operation, input)` triple. The `input` field is free form, and depend on the specific resource and ability being interacted with, and is not described in this specification. @@ -249,22 +255,15 @@ type Task struct { ### 3.2.1 Resource -The Resource `(uri`) field MUST contain the [URI] of the resource being accessed. If the resource being accessed is some static data, it is RECOMMENDED to reference it by the [`data`], [`ipfs`], or [`magnet`] URI schemes. +The Resource (`uri`) field MUST contain the [URI] of the resource being accessed. If the resource being accessed is some static data, it is RECOMMENDED to reference it by the [`data`], [`ipfs`], or [`magnet`] URI schemes. -### 3.2.3 Ability +### 3.2.3 Operation -The Ability (sometimes called "operation") field MUST contain a [UCAN Ability]. This field can be thought of as the message or trait being sent to the resource. +The Operation field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a [UCAN Ability], which includes heirarchy, an Operation MUST be fully concrete. ### 3.2.4 Input -The Input field, MAY contain any parameters expected by the URI/Ability pair, which MAY be different between different URIs and Abilities, and is thus left to the executor to define the shape of this data. - -The Input field MUST have an IPLD [map representation][ipld representation], and thus MUST be one of the following: - -1. [Struct] in map representation. -2. [Keyed], [enveloped] or [inline] union. -3. [Unit] in empty map representation. -4. [Map] in map representation. +The Input field, MAY contain any parameters expected by the Resource/Operation pair, which MAY be different between different Resources and Operations, and is thus left to the executor to define the shape of this data. This field MUST be representable as a map or keyword list. UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type of `input`s allowed. @@ -320,7 +319,7 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { - "uri" "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", + "uri": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", "op": "wasm/run", "input": { "func": "add_one", @@ -382,7 +381,7 @@ Concretely, this means that the `&Task` MUST be present in the associated `auth` type Task struct { run &Instruction meta {String : Any} - prf [&UCAN] + prf [&UcanDelegation] # Receipt of the invocation that caused this invocation cause optional &Receipt @@ -403,6 +402,21 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt The `prf` field MUST contain links to any UCANs that provide the authority to perform this task. All of the outermost proofs MUST have `aud` field set to the [Executor]'s DID. All of the outmost proofs MUST have `iss` field set to the [Invoker]'s DID. + + + + + + +FIXME expand to move proofs here + + + + + + + + ### 5.1.4 Optional Cause [Task]s MAY be invoked as an effect caused by a prior [Invocation]. Such [Invocation]s SHOULD have a `cause` field set to the [Receipt] link of the [Invocation] that caused it. The linked [Receipt] MUST have an `Effect` (the `fx` field) containing invoked [Task] in the `run` field. @@ -424,17 +438,7 @@ The [Task] containing the [Instruction] and any configuration. ### 5.2.2 Authorization -The `auth` field MUST contain a [Varsig] signing over an array of CIDs that includes the [Task]'s CID, signed by the issuer of the proofs. - -## 5.3 Invocation Tag - -An invocation capsule associates the [Invocation] with a versioned schema. This field is NOT REQUIRED. Wrapping an [Invocation] in an Invocation Tag is RECOMMENDED in contexts where the schema and version are not clear from context, such as when it is being stores or passed around without being nested in another structure that defines the version in its schema. - -``` -type InvocationTag union { - | Invocation "ucan/invoke@0.2.0" -} representation keyed -``` +The `auth` field MUST contain a signature over an array of CIDs that includes the [Task]'s CID, signed by the issuer of the proofs. ## 5.4 DAG-JSON Example From ee652ef705c2050820fc4405e894499d0ed83732 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 13 Oct 2023 17:14:26 -0700 Subject: [PATCH 038/127] Simplify diagram --- README.md | 840 ++++++++++++++++++------------------------------------ 1 file changed, 281 insertions(+), 559 deletions(-) diff --git a/README.md b/README.md index 46900ebb..7599a042 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,10 @@ -# UCAN Invocation Specification v1.0.0-rc.1 +# UCAN Invocation Specification v1.0.0-rc. 1 + +FIXME +- move pipelines, await, etc to own spec +- explain in FAQ why we don't need `join` (i.e. use promises, since the model is inverted) +- add expiry +- Consider ability heirarcy as being subpaths: `fs/mutate/write` ## Editors @@ -17,7 +23,7 @@ # 0 Abstract -UCAN Invocation defines a format for expressing the intention to execute delegated UCAN capabilities, the attested receipts from an execution, and how to extend computation via promise pipelining. +UCAN Invocation defines a format for expressing the intention to execute delegated UCAN capabilities, and the attested receipts from an execution. ## Language @@ -29,6 +35,10 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S > > — Anonymous +> When authorizaton is communicated without such context, it's like receiving a key in the mail with no hint about what to do with it [...] After an object receives this message, she can invoke arg if she chooses, but why would she ever choose to do so? +> +> Mark Miller, [E-lang Mailing List, 2000 Oct 18] + UCAN is a chained-capability format. A UCAN contains all of the information that one would need to perform some task, and the provable authority to do so. This begs the question: can UCAN be used directly as an RPC language? Some teams have had success with UCAN directly for RPC when the intention is clear from context. This can be successful when there is more information on the channel than the UCAN itself (such as an HTTP path that a UCAN is sent to). However, capability invocation contains strictly more information than delegation: all of the authority of UCAN, plus the command to perform the task. @@ -81,42 +91,35 @@ sequenceDiagram participant Bob participant Carol 📧 participant Dan - participant Erin - Note over Alice 💾, Erin: Delegations + autonumber + + Note over Alice 💾, Dan: Delegations Alice 💾 -->> Bob: Delegate Bob -->> Carol 📧: Delegate Carol 📧 -->> Dan: Delegate Carol 📧 -->> Dan: Delegate - Note over Alice 💾, Erin: Single Invocation - Dan -) Alice 💾: Read from Alice's DB! - Alice 💾 --) Dan: Value - - Note over Alice 💾, Erin: Proxy Execution - Dan -->> Erin: Delegate - Dan -) Erin: Run! - Erin -) Alice 💾: Read from Alice's DB! - Alice 💾 --) Erin: Value - Erin -) Dan: Value - - Note over Alice 💾, Erin: Multiple Invocation Flow - Dan -) Alice 💾: Read from Alice's DB! - Alice 💾 --) Dan: Value - Dan -) Carol 📧: Send email containing as Carol! - Carol 📧 -) Carol 📧: Send email! - - Note over Alice 💾, Erin: Promise Pipeline - Dan -) Alice 💾: Read from Alice's DB! [Invocation ID = 123] - Dan -) Carol 📧: Send email containing Result as Carol! - Alice 💾 --) Carol 📧: Value - Carol 📧 -) Carol 📧: Send email containing as Carol! - + Note over Alice 💾, Dan: Single Invocation + Dan ->> Alice 💾: Read from Alice's DB! + Alice 💾 -->> Dan: Result<➎> + + Note over Alice 💾, Dan: Multiple Invocation Flow + Dan ->> Alice 💾: Read from Alice's DB! + Alice 💾 -->> Dan: Result<➐> + Dan ->> Carol 📧: Send email containing Result<➐> as Carol! + Carol 📧 ->> Carol 📧: Send email! + + Note over Alice 💾, Dan: Promise Pipeline + Dan ->> Alice 💾: Read from Alice's DB! + Dan ->> Carol 📧: Send email containing Result<⓫> as Carol! + Alice 💾 -->> Carol 📧: Result<⓫> + Carol 📧 ->> Carol 📧: Send email containing Result<⓫> as Carol! ``` ## 1.4 Serialization -Unlike [UCAN Delegation]s, Invocations are point-to-point. This means that — aside from an Instruction ID — the exact format need only be agreed on by the two parties directly communicating. The examples in this document are given as [DAG-JSON] (consistent with [`invocation-ipld`]), but any serialization format MAY be used as long as it's accepted by both parties. +Unlike [UCAN Delegation]s, Invocations are point-to-point. This means that — aside from an Instruction ID — the exact format need only be agreed on by the two parties directly communicating. The examples in this document are given as [JWT], but others (such as [`invocation-ipld`]) MAY be used as long as it's accepted by both parties. ## 1.5 Public Resources @@ -149,15 +152,19 @@ The executor MUST be the UCAN delegate. Their DID MUST be set the in `aud` field ### 2.2.1 Instruction +FIXME merge Instrction and task? + An [Instruction] is like a deferred function application: a request to perform some action on a resource with specific input. ### 2.2.2 Task A [Task] wraps an [Instruction] with runtime configuration, including timeouts, fuel, trace metadata, and so on. + ### 2.2.3 Invocation @@ -177,15 +184,17 @@ An [Effect] is the instruction to the [Executor] to enqueue a new set of [Task]s # 3 Instruction +FIXME rename? + An Instruction is the smallest unit of work that can be requested from a UCAN. It describes one `(resource, operation, input)` triple. The `input` field is free form, and depend on the specific resource and ability being interacted with, and is not described in this specification. Using the JavaScript analogy from the introduction, an Instruction is similar to wrapping a call in a closure: ```json { - "uri" "mailto:alice@example.com", - "op": "msg/send", - "input": { + "act": "msg/send", + "arg": { + "from": "mailto:alice@example.com", "to": [ "bob@example.com", "carol@example.com" @@ -199,15 +208,175 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to ```js // Pseudocode () => - msg.send("mailto:alice@example.com", { + cast(alice, msg.send({ + from: "mailto:alice@example.com", to: ["bob@example.com", "carol@example.com"], subject: "hello", body: "world" }) ``` + + + + + + + + + + + + +```json +{ + alg: "EdDSA", + typ: "JWT" +} +{ + iss: "did:example:bob", + aud: "did:example:alice", + meta: { + fuel: 100, + disk: "100GB" + }, + run: { + "act": "msg/send", + "arg": { + "from": "mailto:alice@example.com", + "to": [ + "bob@example.com", + "carol@example.com" + ], + "subject": "hello", + "body": "world" + }, + prf: [bafy1, bafy2] + } +} + + + +// + +{ + alg: "EdDSA", + typ: "JWT" +} +{ + iss: "did:example:bob", + aud: "did:example:alice", + meta: { + fuel: 100, + disk: "100GB" + }, + run: { + "act": "ipvm/workflow/run", + "arg": { + steps: [bafyX, bafyY, bafyZ], + inlineSteps: [ + { + "act": "msg/send", + "arg": { + "from": "mailto:alice@example.com", + "to": [ + "bob@example.com", + "carol@example.com" + ], + "subject": "hello", + "body": "world" + }, + prf: [bafy1, bafy2] + }, + { + "act": "wasm/run", + "arg": { + "mod": "ipfs://...", + "fun": "add_one", + "arg": [42] + } + }, + { + "sub": "did:web:myDatabaseService", // <- where + "act": "crud/update", + "arg": { + "" + } + } + ] + prf: [bafy1, bafy3] + } + } +} +``` + + + + +``` js +{ + iss: "did:example:bob", + aud: "did:example:alice", + task: { + run: { + sub: "did:example:alice", // <- where, IFF the subject is relevant... only really useful for + act: "counter/inc", + arg: {} + }, + meta: {}, + prf: [bafy1, bafy3], + }, + exp: 999999 // Doubles as a handy timeout +} +``` + +``` js +{ + iss: "did:example:bob", + aud: "did:example:alice", // NOTE: can be ANYONE in the delegation chain for proxying if you don't have a direct path? + exp: 999999, + run: { + sub: null, + act: "wasm/run", + arg: { + mod: "ipfs://...", + fun: "add_one", + arg: [42] + } + }, + prf: [bafy1, bafy3] +} +``` + + +NOTE TO SELF: keep aud field as optional. i.e. make it salient for ergonomic reasons / push it into the task writer's face. also aud & sub MAY diverge in the future. + + + +FIXME showdelegated execution off a queue + + +FIXME change operation to action in other specs? +FIXME bring back constructivity in delegations becauyse it makes invocation easier to check? + + + + + + + + + + -## 3.1 Schema -``` -type Task struct { - uri URI - op Ability - input {String : Any} - nnc String -} -``` +## 3.1 Fields -## 3.2 Fields +| Field | Type | Description | Required | +|-----------|---------------------|-------------|----------| +| Operation | String | | Yes | +| Input | Map `{String: Any}` | | Yes | +| Nonce | String | | Yes | -### 3.2.1 Resource +### 3.1.1 Resource The Resource (`uri`) field MUST contain the [URI] of the resource being accessed. If the resource being accessed is some static data, it is RECOMMENDED to reference it by the [`data`], [`ipfs`], or [`magnet`] URI schemes. -### 3.2.3 Operation +### 3.1.3 Action -The Operation field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a [UCAN Ability], which includes heirarchy, an Operation MUST be fully concrete. +The Action (`act`) field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a [UCAN Ability], which includes heirarchy, an Operation MUST be fully concrete. -### 3.2.4 Input +### 3.1.4 Arguments -The Input field, MAY contain any parameters expected by the Resource/Operation pair, which MAY be different between different Resources and Operations, and is thus left to the executor to define the shape of this data. This field MUST be representable as a map or keyword list. +The Arguments (`arg`) field, MAY contain any parameters expected by the Resource/Operation pair, which MAY be different between different Resources and Operations, and is thus left to the executor to define the shape of this data. This field MUST be representable as a map or keyword list. UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type of `input`s allowed. If the `input` field is not present, it is implicitly a `unit` represented as empty map. -### 3.2.6 Nonce +### 3.1.6 Nonce If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in ASCII. This field ensures that multiple invocations are unique. -## 3.3 DAG-JSON Examples +## 3.2 DAG-JSON Examples -### 3.3.1 Interacting with an HTTP API +### 3.2.1 Interacting with an HTTP API ```json { - "uri" "https://example.com/blog/posts", - "op": "crud/create", - "input": { + "act": "crud/create", + "arg": { + "uri" "https://example.com/blog/posts", "headers": { "content-type": "application/json" }, @@ -298,13 +464,13 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS } ``` -### 3.3.2 Sending Email +### 3.2.2 Sending Email ```json { - "uri" "mailto:akiko@example.com", - "op": "msg/send", - "input": { + "act": "msg/send", + "arg": { + "from" "mailto:akiko@example.com", "to": [ "boris@example.com", "carol@example.com" @@ -315,13 +481,13 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS } ``` -### 3.3.3 Running WebAssembly +### 3.2.3 Running WebAssembly ```json { - "uri": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", - "op": "wasm/run", - "input": { + "act": "wasm/run", + "arg": { + "mod": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", "func": "add_one", "input": [ 42 @@ -330,6 +496,11 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS } ``` + # 5 Task @@ -381,14 +553,14 @@ Concretely, this means that the `&Task` MUST be present in the associated `auth` type Task struct { run &Instruction meta {String : Any} - prf [&UcanDelegation] + prf [&Delegation] # Receipt of the invocation that caused this invocation cause optional &Receipt } ``` -### 5.1.1 Instruction +### 5.1.1 Task The `run` field MUST contain a link to the [Task] to be run. @@ -436,117 +608,6 @@ type Invocation struct { The [Task] containing the [Instruction] and any configuration. -### 5.2.2 Authorization - -The `auth` field MUST contain a signature over an array of CIDs that includes the [Task]'s CID, signed by the issuer of the proofs. - -## 5.4 DAG-JSON Example - -### 5.4.1 Single Invocation - -```json -{ - "_": { - "ucan/invocation@0.2.0": "bafy...invocation" - }, - "bafy...invocation": { - "task": { - "run": {"/": "bafy...createBlogPost"}, - "prf": [{"/": "bafy...ucanProof"}] - }, - "auth": {"/": "bafy...auth"} - }, - "bafy...auth": { - "scope": [{"/": "bafy...createBlogPostTask"}], - "s": {"/": {"bytes": "7aEDQPPhXNvtVb5/T+O40xXU6TSgJZDFnlVaV3GMlaEo/dvxtyaCLm8uUsFK4xzQsQd82QQUYA6fK506XqjghRlucAQ"}} - }, - "bafy...createBlogPost": { - "uri" "https://example.com/blog/posts", - "op": "crud/create", - "input": { - "headers": { - "content-type": "application/json" - }, - "payload": { - "title": "How UCAN Tasks Changed My Life", - "body": "This is the story of how one spec changed everything...", - "topics": [ - "authz", - "journal" - ], - "draft": true - } - } - } -} -``` - -### 5.4.2 Multiple Invocations - -```json -{ - "bafy...createBlogPostInstruction": { - "uri" "https://example.com/blog/posts", - "op": "crud/create", - "input": { - "headers": { - "content-type": "application/json" - }, - "payload": { - "title": "How UCAN Tasks Changed My Life", - "body": "This is the story of how one spec changed everything...", - "topics": [ - "authz", - "journal" - ], - "draft": true - } - } - }, - "bafy...sendEmailInstruction": { - "uri" "mailto:akiko@example.com", - "op": "msg/send", - "input": { - "to": [ - "boris@example.com", - "carol@example.com" - ], - "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!", - "subject": "Coffee" - } - }, - "bafy...createBlogPostTask": { - "task": { - "run": {"/": "bafy...createBlogPostTask"}, - "prf": [{"/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a"}] - }, - "sig": {"/": "bafy...multipleAuth"} - }, - "bafy...sendEmailTask": { - "task": { - "run": {"/": "bafy...sendEmailTask"}, - "prf": [ - {"/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4"} - ] - }, - "sig": {"/": "bafy...multipleAuth"}, - }, - "bafy...sendEmailInvocation": { - "task": {"/": "bafy...sendEmailTask"}, - "auth": {"/": "bafy...multipleAuth"} - }, - "bafy...createBlogPostInvocation": { - "task": {"/": "bafy...createBlogPostTask"}, - "auth": {"/": "bafy...multipleAuth"} - }, - "bafy...multipleAuth": { - "scope": [ - {"/": "bafy...createBlogPostTask"}, - {"/": "bafy...sendEmailTask"} - ], - "s": {"/": {"bytes": "7aEDQKxIrga+88HNDd69Ho4Ggz8zkf+GxWC6dAGYua6l85YgiL3NqGxyGAygiSZtWrWUo6SokgOys2wYE7N+novtcwo"}} -} -``` ### 5.4.3 Causal Invocations @@ -637,10 +698,6 @@ Tasks in the `fork` field MAY be related to the Task in the `join` field if ther type Effects { # Primary set of tasks to be invoked fork [&Invocation] - - # Additional task to be invoked with added semantics - # of representing a workflow execution continuation. - join optional &Invocation } ``` @@ -874,6 +931,48 @@ A Receipt Tag associates the `Receipt` with a versioned schema. This MAY be omit } ``` + + + + + + + + + + + + + + + + + + + + + + +FIXME Split into new spec + + + + + + + + + + + + + + + + + + + # 9 Pipelines > Machines grow faster and memories grow larger. But the speed of light is constant and New York is not getting any closer to Tokyo. As hardware continues to improve, the latency barrier between distant machines will increasingly dominate the performance of distributed computation. When distributed computational steps require unnecessary round trips, compositions of these steps can cause unnecessary cascading sequences of round trips @@ -964,387 +1063,6 @@ Any [Task] field other besides `op` MAY be substituted with `Await`. The `op` fi An [Await] MAY be used across [Invocation]s with a same [Authorization], or across [Invocation]s with different [Authorization] and MAY even be across multiple Invokers and Executors. As long as the invocation can be resolved, it MAY be promised. This is sometimes referred to as ["promise pipelining"]. -## Await - -An `Await` describes the eventual output of the referenced [Task] invocation. An `Await` MUST resolve to an output [Result] with `await/*` variant. If unwrapping success or failure case is desired, corresponding `await/ok` or `await/error` variants MUST be used. - -### 9.1 Schema - -``` -type Promise union { - | &Instruction "await/*" - | &Instruction "await/ok" - | &Instruction "await/error" -} representation keyed -``` - -#### 9.2 Variants - -##### 9.2.1 Success - -The successful output of the [Instruction] MAY be referenced by wrapping the [Instruction] in the `"await/ok"` tag. - -The [Executor] MUST fail an [Instruction] that `Await`s successful output of the failed [Instruction]. - -Upon learning of a successful execution of an awaited `Instruction`, the [Executor] MUST substitute the [Instruction] field set with the (unwrapped) `ok` value of the output. - -##### 9.2.2 Failure - -The failed output of the [Task] MAY be referenced by wrapping the [Task] in the `"await/error"` tag. - -[Executor] MUST fail [Task] that `Await`s failed output of the successful [Task]. - -[Executor] MUST substitute [Task] field set to the [Await] of the failed [Task] with an (unwrapped) `error` value of the output. - -##### 9.2.3 Result - -The [Result] output of the [Task] MAY be reference by wrapping the [Task] in the `"await/*"` tag. - -[Executor] MUST substitute [Task] field set to the [Await] of the [Task] with a `Result` value of the output. - -## 9.3 Dataflow - -Pipelining uses [Await] as arguments to determine the required dataflow graph. The following examples both express the following dataflow graph: - -### 9.3.1 Batched - -```mermaid -flowchart BR - update-dns("with: dns:example.com?TYPE=TXT - do: crud/update") - notify-bob("with: mailto://alice@example.com - do: msg/send - to: bob@example.com") - notify-carol("with: mailto://alice@example.com - do: msg/send - to: carol@example.com") - - log-as-done("with: https://example.com/report - do: crud/update") - - update-dns --> notify-bob --> log-as-done - update-dns --> notify-carol --> log-as-done -``` - -```json -{ - "bafy...updateDnsTask": { - "uri" "dns:example.com?TYPE=TXT", - "op": "crud/update", - "input": { - "value": "hello world" - } - }, - "bafy...sendBobEmailTask": { - "uri" "mailto://alice@example.com", - "op": "msg/send", - "input": { - "to": "bob@example.com", - "subject": "DNSLink for example.com", - "body": { - "await/ok": { - "/": "bafy...updateDnsTask" - } - } - } - }, - "bafy...sendCarolEmailTask": { - "uri" "mailto://alice@example.com", - "op": "msg/send", - "input": { - "to": "carol@example.com", - "subject": "Hey Carol, DNSLink was updated!", - "body": { - "await/ok": { - "/": "bafy...updateDnsTask" - } - } - } - }, - "bafy...updateReportTask": { - "uri" "https://example.com/report", - "op": "crud/update", - "input": { - "payload": { - "from": "mailto://alice@exmaple.com", - "to": [ - "bob@exmaple.com", - "carol@example.com" - ], - "event": "email-notification" - }, - "_": [ - { - "await/ok": { - "/": "bafy...sendBobEmailTask" - } - }, - { - "await/ok": { - "/": "bafy...sendCarolEmailTask" - } - } - ] - } - }, - "bafy...auth": { - "scope": [ - { - "/": "bafy...updateDnsTask" - }, - { - "/": "bafy...sendBobEmailTask" - }, - { - "/": "bafy...sendCarolEmailTask" - }, - { - "/": "bafy...updateReportTask" - } - ], - "s": { - "/": { - "bytes": "7aEDQLbVVvN/RU8juyz+r36xMgCP1Eh1OknVckuCPrkTmvGS+ULTtCcvjF3gCqpqf6As7VLewoqTvWX1sswRudmOvAY" - } - } - }, - "bafy...updateDnsInvocation": { - "run": { - "/": "bafy...updateDnsTask" - }, - "auth": { - "/": "bafy...auth" - }, - "prf": [ - { - "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" - } - ] - }, - "bafy...sendBobEmailInvocation": { - "run": { - "/": "bafy...sendBobEmailTask" - }, - "auth": { - "/": "bafy...auth" - }, - "prf": [ - { - "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" - } - ] - }, - "bafy...sendCarolEmailInvocation": { - "run": { - "/": "bafy...sendCarolEmailTask" - }, - "auth": { - "/": "bafy...auth" - }, - "prf": [ - { - "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" - } - ] - }, - "bafy...updateReportInvocation": { - "run": { - "/": "bafy...updateReportTask" - }, - "auth": { - "/": "bafy...auth" - }, - "prf": [ - { - "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" - } - ] - } -} -``` - -### 9.4.2 Serial - -```mermaid -flowchart TB - update-dns("with: dns:example.com?TYPE=TXT - do: crud/update") - notify-bob("with: mailto://alice@example.com - do: msg/send - to: bob@example.com") - notify-carol("with: mailto://alice@example.com - do: msg/send - to: carol@example.com") - - log-as-done("with: https://example.com/report - do: crud/update") - - subgraph start [ ] - update-dns - notify-bob - end - - subgraph finish [ ] - notify-carol - log-as-done - end - - update-dns -.-> notify-bob - update-dns --> notify-carol - notify-bob --> log-as-done - notify-carol -.-> log-as-done -``` - -```json -{ - "bafy...updateDnsTask": { - "uri" "dns:example.com?TYPE=TXT", - "op": "crud/update", - "input": { - "value": "hello world" - } - }, - "bafy...sendBobEmailTask": { - "uri" "mailto://alice@example.com", - "op": "msg/send", - "input": { - "to": "bob@example.com", - "subject": "DNSLink for example.com", - "body": { - "await/ok": { - "/": "bafy...updateDnsTask" - } - } - } - }, - "bafy...updateDnsInvocation": { - "run": { - "/": "bafy...updateDnsInvocation" - }, - "auth": { - "/": "bafy...authForBatchOne" - }, - "prf": [ - { - "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" - } - ] - }, - "bafy...sendBobEmailInvocation": { - "run": { - "/": "bafy...sendBobEmailTask" - }, - "auth": { - "/": "bafy...authForBatchOne" - }, - "prf": [ - { - "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" - } - ] - }, - "bafy...authForBatchOne": { - "scope": [ - { - "/": "bafy...updateDnsTask" - }, - { - "/": "bafy...sendBobEmailTask" - } - ], - "s": { - "/": { - "bytes": "7aEDQG2GvLnr2gVEfMDrEUV8S3fw8JuFGVKAGIhSZCqCmHGyQ8cdU2A/Vp97yAsZQ+tqBaMWN3Q6YJLfPpAdgaXf2gY" - } - } - } -} -``` - -```json -{ - "bafy...emailCarolTask": { - "uri" "mailto://alice@example.com", - "op": "msg/send", - "input": { - "to": "carol@example.com", - "subject": "Hey Carol, DNSLink was updated!", - "body": { - "await/ok": { - "/": "bafy...updateDnsTask" - } - } - } - }, - "bafy...updateReportTask": { - "uri" "https://example.com/report", - "op": "crud/update", - "input": { - "payload": { - "from": "mailto://alice@exmaple.com", - "to": [ - "bob@exmaple.com", - "carol@example.com" - ], - "event": "email-notification" - }, - "_": [ - { - "await/ok": { - "/": "bafy...emailBobTask" - } - }, - { - "await/ok": { - "/": "bafy...emailCarolTask" - } - } - ] - } - }, - "bafy...authForSecondBatch": { - "scope": [ - { - "/": "bafy...emailCarolTask" - }, - { - "/": "bafy...updateReportInvocation" - } - ], - "s": { - "/": { - "bytes": "7aEDQM1yNTEO/+TF69wUwteH+ftAjD0ik5tXDa+sheAiuOZobSco/+vU882/Nf3LtMRF1EDoP/H38PX2bD5nJzkHAAU" - } - } - }, - "bafy...emailCarolInvocation": { - "run": { - "/": "bafy...emailCarolTask" - }, - "auth": { - "/": "bafy...authForSecondBatch" - }, - "prf": [ - { - "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" - } - ] - }, - "bafy...updateReportInvocation": { - "run": { - "/": "bafy...updateReporttask" - }, - "auth": { - "/": "bafy...authForSecondBatch" - }, - "prf": [ - { - "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" - } - ] - } -} -``` # 10 Prior Art @@ -1401,3 +1119,7 @@ Thanks to [Christine Lemmer-Webber] for the many conversations about capability [eRights]: https:/erights.org [promise pipelining]: http://erights.org/elib/distrib/pipeline.html [ucanto RPC]: https://github.com/web3-storage/ucanto + + + +[E-lang Mailing List, 2000 Oct 18]: http://wiki.erights.org/wiki/Capability-based_Active_Invocation_Certificates From e7d75800cd1f932e7b563f01628f22e62962a205 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 13 Oct 2023 23:35:18 -0700 Subject: [PATCH 039/127] Heavily WIP --- README.md | 630 +++++++++++++++--------------------------------------- 1 file changed, 168 insertions(+), 462 deletions(-) diff --git a/README.md b/README.md index 7599a042..8541267e 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ FIXME - explain in FAQ why we don't need `join` (i.e. use promises, since the model is inverted) - add expiry - Consider ability heirarcy as being subpaths: `fs/mutate/write` +- operation -> command, Instruction -> action ## Editors @@ -125,13 +126,13 @@ Unlike [UCAN Delegation]s, Invocations are point-to-point. This means that — a A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible and -# 2 High-Level Concepts +FIXME Open HTTP GET doensn't need a prf, but can still use invocation -## 2.1 Roles +# 2 Roles Task adds two new roles to UCAN: invoker and executor. The existing UCAN delegator and delegate principals MUST persist to the invocation. -| UCAN Field | Delegation | Task | +| UCAN Field | Delegation | Invocation | | ---------- | -------------------------------------- | ------------------------------- | | `iss` | Delegator: transfer authority (active) | Invoker: request task (active) | | `aud` | Delegate: gain authority (passive) | Executor: perform task (active) | @@ -142,47 +143,41 @@ The invoker signals to the executor that a task associated with a UCAN SHOULD be The invoker MUST be the UCAN delegator. Their DID MUST be authenticated in the `iss` field of the contained UCAN. +### Subject + ### 2.1.2 Executor The executor is directed to perform some task described in the UCAN by the invoker. The executor MUST be the UCAN delegate. Their DID MUST be set the in `aud` field of the contained UCAN. -## 2.2 Components - -### 2.2.1 Instruction - -FIXME merge Instrction and task? +## 3 Components -An [Instruction] is like a deferred function application: a request to perform some action on a resource with specific input. +### 3.1 Command -### 2.2.2 Task +A [Command] is like a deferred function application: a request to perform some action on a resource with specific input. -A [Task] wraps an [Instruction] with runtime configuration, including timeouts, fuel, trace metadata, and so on. +### 3.2 Task - - -### 2.2.3 Invocation +### 3.3 Invocation An [Invocation] is an [Authorized] [Task]. -### 2.2.4 Result +### 3.4 Result -A [Result] is the output of an [Instruction]. +A [Result] is the output of an [Command]. -### 2.2.5 Receipt +### 3.5 Receipt A [Receipt] is a cryptographically signed description of the [Invocation] output and requested [Effect]s. -### 2.2.6 Effect +### 3.6 Effect An [Effect] is the instruction to the [Executor] to enqueue a new set of [Task]s. -# 3 Instruction +# 3 Command FIXME rename? @@ -216,18 +211,6 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to }) ``` - - - - - - - - - - - - ```json { alg: "EdDSA", @@ -236,79 +219,26 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to { iss: "did:example:bob", aud: "did:example:alice", + act: "msg/send", + nnc: "", + arg: { + from: "mailto:alice@example.com", + to: [ + "bob@example.com", + "carol@example.com" + ], + subject: "hello", + body: "world" + } meta: { fuel: 100, disk: "100GB" }, - run: { - "act": "msg/send", - "arg": { - "from": "mailto:alice@example.com", - "to": [ - "bob@example.com", - "carol@example.com" - ], - "subject": "hello", - "body": "world" - }, - prf: [bafy1, bafy2] - } + prf: [bafy1, bafy2] } -// - -{ - alg: "EdDSA", - typ: "JWT" -} -{ - iss: "did:example:bob", - aud: "did:example:alice", - meta: { - fuel: 100, - disk: "100GB" - }, - run: { - "act": "ipvm/workflow/run", - "arg": { - steps: [bafyX, bafyY, bafyZ], - inlineSteps: [ - { - "act": "msg/send", - "arg": { - "from": "mailto:alice@example.com", - "to": [ - "bob@example.com", - "carol@example.com" - ], - "subject": "hello", - "body": "world" - }, - prf: [bafy1, bafy2] - }, - { - "act": "wasm/run", - "arg": { - "mod": "ipfs://...", - "fun": "add_one", - "arg": [42] - } - }, - { - "sub": "did:web:myDatabaseService", // <- where - "act": "crud/update", - "arg": { - "" - } - } - ] - prf: [bafy1, bafy3] - } - } -} -``` @@ -316,16 +246,15 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to ``` js { iss: "did:example:bob", - aud: "did:example:alice", - task: { - run: { - sub: "did:example:alice", // <- where, IFF the subject is relevant... only really useful for - act: "counter/inc", - arg: {} - }, - meta: {}, - prf: [bafy1, bafy3], + aud: "did:example:alice", // Origoinally removed these because it's duplicated from prf, but important for e.g. CRDT + run: { + sub: "did:example:alice", // <- where, IFF the subject is relevant... only really useful for + cmd: "counter/inc", + arg: {by: 4} }, + meta: {}, + cause: {"/": "bafy...123"}, + prf: [bafy1, bafy3], exp: 999999 // Doubles as a handy timeout } ``` @@ -336,7 +265,6 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to aud: "did:example:alice", // NOTE: can be ANYONE in the delegation chain for proxying if you don't have a direct path? exp: 999999, run: { - sub: null, act: "wasm/run", arg: { mod: "ipfs://...", @@ -350,116 +278,130 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to NOTE TO SELF: keep aud field as optional. i.e. make it salient for ergonomic reasons / push it into the task writer's face. also aud & sub MAY diverge in the future. - - - FIXME showdelegated execution off a queue - - -FIXME change operation to action in other specs? FIXME bring back constructivity in delegations becauyse it makes invocation easier to check? +## 3.1 Fields +| Name | Field | Type | Required | Notes | +|-------------|-------|---------------------|----------|--------------| +| [Subject] | `sub` | DID | No | | +| [Action] | `act` | String | Yes | | FIXME or `cmd`? +| [Arguments] | `arg` | Map `{String: Any}` | Yes | | +| [Nonce] | `nnc` | String | Yes | May be empty | +### 3.1.1 Subject +The OPTIONAL `sub` field is intended for cases where parametrizing a specific agent is importnat + +### 3.1.3 Action +The Action (`act`) field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a [UCAN Ability], which includes heirarchy, an Operation MUST be fully concrete. - +type Invocation struct { + uci SemVer + iss DID + aud DID -## 3.1 Fields + run Task + cse optional &Receipt +} -| Field | Type | Description | Required | -|-----------|---------------------|-------------|----------| -| Operation | String | | Yes | -| Input | Map `{String: Any}` | | Yes | -| Nonce | String | | Yes | +type Task struct { + run &Inst + mta {String : Any} + prf [&Delegation] +} -### 3.1.1 Resource +type Inststruct { + act Action + arg {String : Any} -The Resource (`uri`) field MUST contain the [URI] of the resource being accessed. If the resource being accessed is some static data, it is RECOMMENDED to reference it by the [`data`], [`ipfs`], or [`magnet`] URI schemes. + nnc optional String + sub optional DID +} -### 3.1.3 Action +``` -The Action (`act`) field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a [UCAN Ability], which includes heirarchy, an Operation MUST be fully concrete. +``` +type Signed struct { + pld &T + sig Signature +} -### 3.1.4 Arguments +type Signature union { + | inline Bytes + | batch Authorization +} -The Arguments (`arg`) field, MAY contain any parameters expected by the Resource/Operation pair, which MAY be different between different Resources and Operations, and is thus left to the executor to define the shape of this data. This field MUST be representable as a map or keyword list. +type Authorization struct { + scp [&Any] + sig Bytes +} +``` -UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type of `input`s allowed. +``` +type Receipt struct { + iss DID + ran &Invocation -If the `input` field is not present, it is implicitly a `unit` represented as empty map. + enq [&Task] # AKA fx -### 3.1.6 Nonce + out Output -If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in ASCII. This field ensures that multiple invocations are unique. + mta {String : Any} + rec &Signed +} +``` + +// FIXME show how to model join with more tasks +TODO NOTE add JWT sig serialization + sig types to Varsig? -## 3.2 DAG-JSON Examples ### 3.2.1 Interacting with an HTTP API ```json { - "act": "crud/create", - "arg": { - "uri" "https://example.com/blog/posts", - "headers": { - "content-type": "application/json" + // ... + run: { + "act": "crud/create", + "arg": { + "uri" "https://example.com/blog/posts", + "headers": { + "content-type": "application/json" + }, + "payload": { + "title": "How UCAN Tasks Changed My Life", + "body": "This is the story of how one spec changed everything...", + "topics": [ + "authz", + "journal" + ], + "draft": true + } }, - "payload": { - "title": "How UCAN Tasks Changed My Life", - "body": "This is the story of how one spec changed everything...", - "topics": [ - "authz", - "journal" - ], - "draft": true - } + "nnc": "&NCC-1701-D*" } } ``` @@ -476,8 +418,9 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS "carol@example.com" ], "subject": "Coffee", - "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" - } + "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Invocations!" + }, + "nnc": "1234567890" } ``` @@ -485,61 +428,17 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ```json { + "nnc": "", // FIXME infers idempotence "act": "wasm/run", "arg": { "mod": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", - "func": "add_one", - "input": [ - 42 - ] + "fun": "add_one", + "params": [42] } } ``` - - -# 5 Task +# 4 Task As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is an instruction to the [Executor] to perform enclosed [Task]. [Invocation]s are not executable until they have been provided provable authority (in form of UCANs in the `prf` field) and an [Authorization] (in the `auth` field) from the [Invoker]. @@ -547,16 +446,13 @@ The `auth` field MUST be contain an [Authorization] which signs over the `&Task` Concretely, this means that the `&Task` MUST be present in the associated `auth`'s `scope` field. A `Receipt` where the associated [Authorization] does not include the [Task] in the `scope` MUST be considered invalid. -## 5.1 Task +## 4.1 Task ``` type Task struct { - run &Instruction - meta {String : Any} - prf [&Delegation] - - # Receipt of the invocation that caused this invocation - cause optional &Receipt + run &Command + meta {String : Any} + cause optional &Receipt } ``` @@ -593,54 +489,10 @@ FIXME expand to move proofs here [Task]s MAY be invoked as an effect caused by a prior [Invocation]. Such [Invocation]s SHOULD have a `cause` field set to the [Receipt] link of the [Invocation] that caused it. The linked [Receipt] MUST have an `Effect` (the `fx` field) containing invoked [Task] in the `run` field. -## 5.2 (Signed) Invocation - -An [Invocation] is a signed [Task]. - -``` -type Invocation struct { - task Task - auth &Authorization -} -``` - ### 5.2.1 Task The [Task] containing the [Instruction] and any configuration. - -### 5.4.3 Causal Invocations - -```json -{ - "bafy...updateDnsInstruction": { - "uri" "dns:example.com?TYPE=TXT", - "op": "crud/update", - "input": { - "value": "hello world" - } - }, - "bafy...updateDnsTask": { - "run": {"/": "bafy...updateDnsTask"}, - "auth": {"/": "bafy...auth"}, - "cause": {"/": "bafy...somePriorInvocation"}, - "prf": [ - {"/": "bafyreieynwqrabzdhgl652ftsk4mlphcj3bxchkj2aw5eb6dc2wxieilau"} - ] - }, - "bafy...updateDnsInvocation": { - "task": {"/": "bafy...updateDnsTask"}, - "auth": {"/": "bafy...auth"} - }, - "bafy...auth": { - "scope": [ - {"/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny"} - ], - "s": {"/": { "bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}} - }, -} -``` - # 6 Result A Result records the output of the [Task], as well as its success or failure state. @@ -696,65 +548,12 @@ Tasks in the `fork` field MAY be related to the Task in the `join` field if ther ``` # Represents a request to invoke enclosed set of tasks concurrently type Effects { - # Primary set of tasks to be invoked - fork [&Invocation] + fx [&Invocation] } ``` ## 7.2 Fields -### 7.2.1 Forked Task Invocations - -The OPTIONAL `fork` field, if present MUST be a list of an alphabetically ordered [Task] links. List MUST NOT not contain duplicate entries. - -### 7.2.2 Joined Task Invocation - -The OPTIONAL `join` field, if present MUST be set to a [Task] link. - -## 7.3 DAG-JSON Examples - -### 7.3.1 Effect spawning concurrent threads - -```json -{ - "fork": [ - { - "/": "bafyreigmmdzix2vxboojvv6j6h7sgvxnrecdxtglwtqpxw7hybebzlsax4" - }, - { - "/": "bafyreif6gfpzgxnii4ys6a4bjenefg737fb5bgam3onrbmhnoa4llk244q" - } - ] -} -``` - -### 7.3.2 Effect continuing thread execution - -```json -{ - "join": { - "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" - } -} -``` - -### 7.3.1 Effect with fork & join - -```json -{ - "join": { - "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" - }, - "fork": [ - { - "/": "bafyreigmmdzix2vxboojvv6j6h7sgvxnrecdxtglwtqpxw7hybebzlsax4" - }, - { - "/": "bafyreif6gfpzgxnii4ys6a4bjenefg737fb5bgam3onrbmhnoa4llk244q" - } - ] -} -``` # 8 Receipt @@ -771,22 +570,14 @@ type Outcome struct { ran &Invocation # Invocation this is a receipt for out Result # Output of the invocation - fx Effects # Effects to be enqueued + fx [&Task] # Effects to be enqueued meta {String : Any} # All the other metadata - - # Principal that issued this receipt. If omitted issuer is - # inferred from the invocation task audience. - iss optional Principal - - # When issuer is different from executor this MUST hold a UCAN - # delegation chain from executor to the issuer. This should be - # omitted when the executor is the issuer. - prf [&UCAN] - sig Varsig + rec &Outcome # MUST include the } ``` +FIXME make rec work for dag hosue case ### 8.1.1 Ran Invocation @@ -820,20 +611,39 @@ If OPTIONAL `prf` field is present, MUST contain link to UCAN delegation authori The `sig` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without `s` field. The signature MUST be generated by the [Executor] or a delegate if OPTIONAL `iss` field is set. -## 8.2 Receipt Tag - -``` -type ReceiptTag union { - | &Receipt "ucan/receipt@0.2.0" -} representation keyed -``` - A Receipt Tag associates the `Receipt` with a versioned schema. This MAY be omitted in contexts where the schema and version are clear from context (for example, when nested in another structure that defines the version). ## 8.3 DAG-JSON Examples ### 8.3.1 Issued by Executor +``` js +{ + "ran": {"/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu"} + "out": { + type: "ok", + value: { + "members": [ + "bob@example.com", + "alice@web.mail" + ] + } + }, + "meta": { + "retries": 2, + "time": [ + 400, + "hours" + ] + }, + "s": { + "/": { + "bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU" + } + } +} +``` + ```json { "ran": { @@ -953,115 +763,6 @@ A Receipt Tag associates the `Receipt` with a versioned schema. This MAY be omit -FIXME Split into new spec - - - - - - - - - - - - - - - - - - - -# 9 Pipelines - -> Machines grow faster and memories grow larger. But the speed of light is constant and New York is not getting any closer to Tokyo. As hardware continues to improve, the latency barrier between distant machines will increasingly dominate the performance of distributed computation. When distributed computational steps require unnecessary round trips, compositions of these steps can cause unnecessary cascading sequences of round trips -> -> — [Mark Miller], [Robust Composition] - -There MAY not be enough information to described an Invocation at creation time. However, all of the information required to construct the next request in a sequence MAY be available in the same Batch, or in a previous (but not yet complete) Invocation. - -Invocations MAY require arguments from the output of other invocations. Waiting for each request to complete before proceeding to the next task has a performance impact due to the amount of latency. [Promise pipelining] is a solution to this problem: by referencing a prior invocation, a pipelined invocation can direct the executor to use the output of one invocations into the input of the other. This liberates the invoker from waiting for each step. - -A `Promise` MAY be used as a variable placeholder for a concrete value in a [Task] [Invocation] output, waiting on a previous step to complete. - -For example, consider the following invocation batch: - -```json -{ - "bafy...createBlogPostTask": { - "uri" "https://example.com/blog/posts", - "op": "crud/create", - "input": { - "payload": { - "title": "How UCAN Tasks Changed My Life", - "body": "This is the story of how one spec changed everything..." - } - } - }, - "bafy...getBlogEditorsTask": { - "uri" "https://example.com/users/editors", - "op": "crud/read" - }, - "bafy...sendEmailTask": { - "uri" "mailto:akiko@example.com", - "op": "msg/send", - "input": { - "to": { - "await/ok": { - "/": "bafy...getBlogPostEditorsTask" - } - }, - "subject": "Coffee", - "body": { - "await/ok": { - "/": "bafy...createBlogPostTask" - } - } - } - }, - "bafy...sendEmailInvocation": { - "ctx": { - "run": { - "/": "bafy...sendEmailTask" - }, - "prf": [ - { - "/": "bafy...proofUcanOutsideExample" - } - ] - }, - "sig": { - "/": { - "bytes": "7aEDQDEGkezG7Bcpeknf2UJ7hpqeL1PZodrYYTSwRjqZPf67P4r1lRZvX+6+9gV+wDZUX0DZLMv64n2fPKnjvxrEugE" - } - } - } -} -``` - -By analogy, above examples can be interpreted roughly as follows: - -```js -const createDraft = crud.create("https://example.com/blog/posts", { - payload: { - title: "How UCAN Tasks Changed My Life", - body: "This is the story of how one spec changed everything...", - }, -}) - -const getEditors = crud.read("https://example.com/users/editors") - -const notify = msg.send("mailto:akiko@example.com", { - to: (await createDraft).ok, - subject: "Coffee", - body: (await getEditors).ok, -}) -``` - -Any [Task] field other besides `op` MAY be substituted with `Await`. The `op` field is critical in understanding what kind of action will be performed and CAN NOT be substituted with `Await`. - -An [Await] MAY be used across [Invocation]s with a same [Authorization], or across [Invocation]s with different [Authorization] and MAY even be across multiple Invokers and Executors. As long as the invocation can be resolved, it MAY be promised. This is sometimes referred to as ["promise pipelining"]. # 10 Prior Art @@ -1123,3 +824,8 @@ Thanks to [Christine Lemmer-Webber] for the many conversations about capability [E-lang Mailing List, 2000 Oct 18]: http://wiki.erights.org/wiki/Capability-based_Active_Invocation_Certificates + + + +FIXME show proxy invocation +FIXME show delegated execution (work stealing) example From 86bcdbdc2484e716e2afc3fc4d5967b69307aa22 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 14 Oct 2023 10:05:42 -0700 Subject: [PATCH 040/127] Add Schemata --- README.md | 45 --------------------------------------------- invocation.ipldsch | 28 ++++++++++++++++++++++++++++ receipt.ipldsch | 17 +++++++++++++++++ signature.ipldsch | 9 +++++++++ 4 files changed, 54 insertions(+), 45 deletions(-) create mode 100644 invocation.ipldsch create mode 100644 receipt.ipldsch create mode 100644 signature.ipldsch diff --git a/README.md b/README.md index 8541267e..9ec6aea6 100644 --- a/README.md +++ b/README.md @@ -317,50 +317,6 @@ FIXME better wording ## 3.2 DAG-JSON Examples -``` -type Invocation struct { - uci SemVer - - iss DID - aud DID - - run Task - cse optional &Receipt -} - -type Task struct { - run &Inst - mta {String : Any} - prf [&Delegation] -} - -type Inststruct { - act Action - arg {String : Any} - - nnc optional String - sub optional DID -} - -``` - -``` -type Signed struct { - pld &T - sig Signature -} - -type Signature union { - | inline Bytes - | batch Authorization -} - -type Authorization struct { - scp [&Any] - sig Bytes -} -``` - ``` type Receipt struct { iss DID @@ -378,7 +334,6 @@ type Receipt struct { // FIXME show how to model join with more tasks TODO NOTE add JWT sig serialization + sig types to Varsig? - ### 3.2.1 Interacting with an HTTP API ```json diff --git a/invocation.ipldsch b/invocation.ipldsch new file mode 100644 index 00000000..8f896019 --- /dev/null +++ b/invocation.ipldsch @@ -0,0 +1,28 @@ +type SignedInvocation struct { + pld &Invocation + sig Signature +} + +type Invocation struct { + uiv SemVer + + iss DID + aud DID + + run &Task + cse optional &Receipt +} + +type Task struct { + run &Operation + mta {String : Any} + prf [&Delegation] +} + +type Operation { + act Action + arg {String : Any} + + nnc optional String + sub optional DID +} diff --git a/receipt.ipldsch b/receipt.ipldsch new file mode 100644 index 00000000..d4c298ad --- /dev/null +++ b/receipt.ipldsch @@ -0,0 +1,17 @@ +type SignedReceipt { + pld &Receipt + sig Signature +} + +type Receipt struct { + iss DID + ran &Invocation + + enq [&Task] + out Output + + mta {String : Any} + rec &SignedReceipt +} + +FIXME show how to rec diff --git a/signature.ipldsch b/signature.ipldsch new file mode 100644 index 00000000..e4ab4b70 --- /dev/null +++ b/signature.ipldsch @@ -0,0 +1,9 @@ +type Signature union { + | inline Bytes + | batch Authorization +} + +type Authorization struct { + scp [&Any] + sig Bytes +} From c030cd9af34a198f3e4863acd073be9d69fc4aec Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 14 Oct 2023 12:38:56 -0700 Subject: [PATCH 041/127] Lots of reworking & realignment --- README.md | 209 ++++++++++----------------------------------- invocation.ipldsch | 11 +-- receipt.ipldsch | 5 +- signature.ipldsch | 4 +- 4 files changed, 56 insertions(+), 173 deletions(-) diff --git a/README.md b/README.md index 9ec6aea6..d6d117b9 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ # UCAN Invocation Specification v1.0.0-rc. 1 FIXME +FIXME show proxy invocation +FIXME show delegated execution (work stealing) example - move pipelines, await, etc to own spec - explain in FAQ why we don't need `join` (i.e. use promises, since the model is inverted) -- add expiry -- Consider ability heirarcy as being subpaths: `fs/mutate/write` - operation -> command, Instruction -> action +- Be clear that you MUST sign ## Editors @@ -151,7 +152,7 @@ The executor is directed to perform some task described in the UCAN by the invok The executor MUST be the UCAN delegate. Their DID MUST be set the in `aud` field of the contained UCAN. -## 3 Components +## 3 Concepts ### 3.1 Command @@ -173,10 +174,6 @@ A [Result] is the output of an [Command]. A [Receipt] is a cryptographically signed description of the [Invocation] output and requested [Effect]s. -### 3.6 Effect - -An [Effect] is the instruction to the [Executor] to enqueue a new set of [Task]s. - # 3 Command FIXME rename? @@ -203,7 +200,7 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to ```js // Pseudocode () => - cast(alice, msg.send({ + send(alice, msg.send({ from: "mailto:alice@example.com", to: ["bob@example.com", "carol@example.com"], subject: "hello", @@ -212,10 +209,6 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to ``` ```json -{ - alg: "EdDSA", - typ: "JWT" -} { iss: "did:example:bob", aud: "did:example:alice", @@ -278,16 +271,13 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to NOTE TO SELF: keep aud field as optional. i.e. make it salient for ergonomic reasons / push it into the task writer's face. also aud & sub MAY diverge in the future. -FIXME showdelegated execution off a queue -FIXME bring back constructivity in delegations becauyse it makes invocation easier to check? - ## 3.1 Fields | Name | Field | Type | Required | Notes | |-------------|-------|---------------------|----------|--------------| | [Subject] | `sub` | DID | No | | -| [Action] | `act` | String | Yes | | FIXME or `cmd`? +| [Command] | `cmd` | String | Yes | | | [Arguments] | `arg` | Map `{String: Any}` | Yes | | | [Nonce] | `nnc` | String | Yes | May be empty | @@ -297,9 +287,9 @@ The OPTIONAL `sub` field is intended for cases where parametrizing a specific ag -### 3.1.3 Action +### 3.1.3 Command -The Action (`act`) field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a [UCAN Ability], which includes heirarchy, an Operation MUST be fully concrete. +The Command (`cmd`) field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a [UCAN Ability], which includes heirarchy, an Operation MUST be fully concrete. ### 3.1.4 Arguments @@ -315,25 +305,6 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS FIXME better wording -## 3.2 DAG-JSON Examples - -``` -type Receipt struct { - iss DID - ran &Invocation - - enq [&Task] # AKA fx - - out Output - - mta {String : Any} - rec &Signed -} -``` - -// FIXME show how to model join with more tasks -TODO NOTE add JWT sig serialization + sig types to Varsig? - ### 3.2.1 Interacting with an HTTP API ```json @@ -486,7 +457,7 @@ If no information is available, this field SHOULD be set to `{}`. } ``` -## 7 Effect +## 7 Effects The result of an [Invocation] MAY include a request for further actions to be performed via "effects". This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. @@ -520,53 +491,15 @@ Receipts MUST use the same version as the invocation that they contain. ## 8.1 Receipt -``` -type Outcome struct { - ran &Invocation # Invocation this is a receipt for - - out Result # Output of the invocation - fx [&Task] # Effects to be enqueued - - meta {String : Any} # All the other metadata - - rec &Outcome # MUST include the -} -``` -FIXME make rec work for dag hosue case - -### 8.1.1 Ran Invocation - -The `ran` field MUST include a link to the [Invocation] that the Receipt is for. - -### 8.1.2 Output - -The `out` field MUST contain the value output of the invocation in [Result] format. - -### 8.1.3 Effect - -The OPTIONAL `fx` field, if present MUST be set to the caused [Effect]. The [Executor] SHOULD invoke contained [Task] to progress a workflow execution. - -If `fx` does not contain OPTIONAL `join` field, it denotes completion of the current execution thread. - -### 8.1.4 Metadata Fields - -The OPTIONAL metadata field MAY be omitted or used to contain additional data about the receipt. This field MAY be used for tags, commentary, trace information, and so on. - -### 8.1.5 Receipt Issuer - -The OPTIONAL `iss` field, if present MUST contain the DID of the [Executor] delegate that signed it. If field is present, delegation from [Executor] MUST be included in the `prf` field. - -If `iss` field is omitted, Receipt MUST be signed by the [Executor]. - -### 8.1.6 Proofs - -If OPTIONAL `prf` field is present, MUST contain link to UCAN delegation authorizing Receipt Issuer (`iss`) to carry [Task] execution. - -### 8.1.7 Signature - -The `sig` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without `s` field. The signature MUST be generated by the [Executor] or a delegate if OPTIONAL `iss` field is set. - -A Receipt Tag associates the `Receipt` with a versioned schema. This MAY be omitted in contexts where the schema and version are clear from context (for example, when nested in another structure that defines the version). +| Field | Type | Required | Description | +|-------|------------------|----------|------------------------------------------------------------------------------------| +| `iss` | `DID` | Yes | The DID of the Executor | +| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | +| `ran` | `&Invocation` | Yes | MUST be a link to the [Invocation] that the Receipt is for | +| `out` | `Result` | Yes | MUST contain the value output of the invocation in [Result] format | +| `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | +| `mta` | `{String : Any}` | Yes | Additional data about the receipt | +| `rec` | `&Receipt` | No | Recursive `Signed`s if the Invocation was proxied to another Executor | ## 8.3 DAG-JSON Examples @@ -576,22 +509,21 @@ A Receipt Tag associates the `Receipt` with a versioned schema. This MAY be omit { "ran": {"/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu"} "out": { - type: "ok", - value: { + "ok": { "members": [ "bob@example.com", "alice@web.mail" ] } }, - "meta": { + "mta": { "retries": 2, "time": [ 400, "hours" ] }, - "s": { + "sig": { "/": { "bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU" } @@ -601,29 +533,17 @@ A Receipt Tag associates the `Receipt` with a versioned schema. This MAY be omit ```json { - "ran": { - "/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu" - }, + "ran": { "/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu" }, "out": { "ok": { - "members": [ - "bob@example.com", - "alice@web.mail" - ] + "members": [ "bob@example.com", "alice@web.mail" ] } }, "meta": { "retries": 2, - "time": [ - 400, - "hours" - ] + "time": [ 400, "hours" ] }, - "s": { - "/": { - "bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU" - } - } + "sig": { "/": { "bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU" } } } ``` @@ -631,68 +551,37 @@ A Receipt Tag associates the `Receipt` with a versioned schema. This MAY be omit ```json { - "ran": { - "/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu" - }, + "iss": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", + "prf": [ + { "/": "bafyreihfgvlol74ugosa5gkzvbsghmq7wiqn4xvgack4uwn4qagrml6p74" }, + { "/": "SOME OTHER CID FIXME" } + ], + "ran": { "/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu" }, "out": { "ok": { - "members": [ - "bob@example.com", - "alice@web.mail" - ] + "members": [ "bob@example.com", "alice@web.mail" ] } }, - "meta": { + "mta": { "retries": 2, - "time": [ - 400, - "hours" - ] + "time": [ 400, "hours" ] }, - "iss": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", - "prf": [ - { - "/": "bafyreihfgvlol74ugosa5gkzvbsghmq7wiqn4xvgack4uwn4qagrml6p74" - } - ], - "s": { - "/": { - "bytes": "7aEDQKxIrga+88HNDd69Ho4Ggz8zkf+GxWC6dAGYua6l85YgiL3NqGxyGAygiSZtWrWUo6SokgOys2wYE7N+novtcwo" - } - } + "sig": { "/": { "bytes": "7aEDQKxIrga+88HNDd69Ho4Ggz8zkf+GxWC6dAGYua6l85YgiL3NqGxyGAygiSZtWrWUo6SokgOys2wYE7N+novtcwo" } } } ``` -### 8.3.3 Receipt with effects +### 8.3.3 Receipt with Enqueued Tasks ```json { - "ran": { - "/": "bafyreig3qnao4suz3lchh4joof7fhlobmgxhaal3vw4vtcghtlgtp7u4xy" - }, - "out": { - "ok": { - "status": 200 - } - }, - "fx": { - "join": { - "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" - }, - "fork": [ - { - "/": "bafyreigmmdzix2vxboojvv6j6h7sgvxnrecdxtglwtqpxw7hybebzlsax4" - }, - { - "/": "bafyreif6gfpzgxnii4ys6a4bjenefg737fb5bgam3onrbmhnoa4llk244q" - } - ] - }, - "s": { - "/": { - "bytes": "7aEDQAHWabtCE+QikM3Np94TrA5T8n2yXqy8Uf35hgw0fe5c2Xi1O0h/JgrFmGl2Gsbhfm05zpdQmwfK2f/Sbe00YQE" - } - } + "ran": { "/": "bafyreig3qnao4suz3lchh4joof7fhlobmgxhaal3vw4vtcghtlgtp7u4xy" }, + "out": { "ok": { "status": 200 } }, + "fx": [ + { "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" }, + { "/": "bafyreigmmdzix2vxboojvv6j6h7sgvxnrecdxtglwtqpxw7hybebzlsax4" }, + { "/": "bafyreif6gfpzgxnii4ys6a4bjenefg737fb5bgam3onrbmhnoa4llk244q" } + ], + "sig": { "/": { "bytes": "7aEDQAHWabtCE+QikM3Np94TrA5T8n2yXqy8Uf35hgw0fe5c2Xi1O0h/JgrFmGl2Gsbhfm05zpdQmwfK2f/Sbe00YQE" } } } ``` @@ -756,6 +645,7 @@ Thanks to [Christine Lemmer-Webber] for the many conversations about capability [Bacalhau]: https://www.bacalhau.org/ [Brooklyn Zelenka]: https://github.com/expede/ [DAG House]: https://dag.house +[E-lang Mailing List, 2000 Oct 18]: http://wiki.erights.org/wiki/Capability-based_Active_Invocation_Certificates [Fission]: https://fission.codes/ [Haskell]: https://en.wikipedia.org/wiki/Haskell [IPVM]: https://github.com/ipvm-wg @@ -775,12 +665,3 @@ Thanks to [Christine Lemmer-Webber] for the many conversations about capability [eRights]: https:/erights.org [promise pipelining]: http://erights.org/elib/distrib/pipeline.html [ucanto RPC]: https://github.com/web3-storage/ucanto - - - -[E-lang Mailing List, 2000 Oct 18]: http://wiki.erights.org/wiki/Capability-based_Active_Invocation_Certificates - - - -FIXME show proxy invocation -FIXME show delegated execution (work stealing) example diff --git a/invocation.ipldsch b/invocation.ipldsch index 8f896019..8f64b2c9 100644 --- a/invocation.ipldsch +++ b/invocation.ipldsch @@ -1,6 +1,6 @@ type SignedInvocation struct { pld &Invocation - sig Signature + sig &Signature } type Invocation struct { @@ -14,15 +14,16 @@ type Invocation struct { } type Task struct { - run &Operation + run &Command mta {String : Any} prf [&Delegation] + exp Integer } -type Operation { - act Action +type Action { + cmd Command arg {String : Any} + nnc String - nnc optional String sub optional DID } diff --git a/receipt.ipldsch b/receipt.ipldsch index d4c298ad..fd07007d 100644 --- a/receipt.ipldsch +++ b/receipt.ipldsch @@ -10,8 +10,9 @@ type Receipt struct { enq [&Task] out Output + iat Integer mta {String : Any} + + prf [&Delegation] rec &SignedReceipt } - -FIXME show how to rec diff --git a/signature.ipldsch b/signature.ipldsch index e4ab4b70..34811b61 100644 --- a/signature.ipldsch +++ b/signature.ipldsch @@ -1,9 +1,9 @@ type Signature union { | inline Bytes - | batch Authorization + | batch BatchSignature } -type Authorization struct { +type BatchSignature struct { scp [&Any] sig Bytes } From 2d5a20db21246b80d355bc0a2485bdadfc75719f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 14 Oct 2023 21:39:28 -0700 Subject: [PATCH 042/127] Start cleanup --- README.md | 235 ++++++++++++++++++++++++++------------------- invocation.ipldsch | 6 +- receipt.ipldsch | 13 ++- 3 files changed, 146 insertions(+), 108 deletions(-) diff --git a/README.md b/README.md index d6d117b9..ef7fe343 100644 --- a/README.md +++ b/README.md @@ -125,11 +125,11 @@ Unlike [UCAN Delegation]s, Invocations are point-to-point. This means that — a ## 1.5 Public Resources -A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible and +A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard required for initiating flow (e.g. signup). These cases typically involve a reference passed out of band (such as a web link). Due to [designation without authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain , but this SHOULD NOT be the default behaviour. -FIXME Open HTTP GET doensn't need a prf, but can still use invocation +# 2 Concepts -# 2 Roles +## 2.1 Roles Task adds two new roles to UCAN: invoker and executor. The existing UCAN delegator and delegate principals MUST persist to the invocation. @@ -144,41 +144,52 @@ The invoker signals to the executor that a task associated with a UCAN SHOULD be The invoker MUST be the UCAN delegator. Their DID MUST be authenticated in the `iss` field of the contained UCAN. -### Subject - ### 2.1.2 Executor The executor is directed to perform some task described in the UCAN by the invoker. The executor MUST be the UCAN delegate. Their DID MUST be set the in `aud` field of the contained UCAN. -## 3 Concepts - -### 3.1 Command - -A [Command] is like a deferred function application: a request to perform some action on a resource with specific input. - -### 3.2 Task - -A [Task] wraps a [Command] with runtime configuration, including timeouts, fuel, trace metadata, and so on. - -### 3.3 Invocation +## 2.2 Anatomy -An [Invocation] is an [Authorized] [Task]. +| Concept | Description | +|--------------|-----------------------------------------------------------------------------| +| [Invocation] | A request to perform some action based on [delegated][Delegation] authority | +| [Command] | (Deferred) function application; a description of work to be performed | +| [Task] | Contextual information for a [Command], such as resource limits | +| [Receipt] | The return value from an [Invocation], which may [enqueue] more tasks | +| [Result] | The success value or error information from the run [Invocation] | -### 3.4 Result - -A [Result] is the output of an [Command]. - -### 3.5 Receipt - -A [Receipt] is a cryptographically signed description of the [Invocation] output and requested [Effect]s. +``` mermaid +flowchart RL + subgraph InvocationPayload + subgraph Invocation + subgraph Task + Command + DelegationProofs + end + end + + Signature + end + + subgraph Receipt + subgraph ReceiptPayload + Ran + Result + end + + Signature + end + + Ran -->|references| Invocation +``` # 3 Command -FIXME rename? +A Command is the smallest unit of work that can be Invoked. It is akin to a function call or actor message. -An Instruction is the smallest unit of work that can be requested from a UCAN. It describes one `(resource, operation, input)` triple. The `input` field is free form, and depend on the specific resource and ability being interacted with, and is not described in this specification. +The `input` field is free form, and depend on the specific resource and ability being interacted with, and is not described in this specification. Using the JavaScript analogy from the introduction, an Instruction is similar to wrapping a call in a closure: @@ -200,7 +211,7 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to ```js // Pseudocode () => - send(alice, msg.send({ + msg.send({ from: "mailto:alice@example.com", to: ["bob@example.com", "carol@example.com"], subject: "hello", @@ -208,70 +219,6 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to }) ``` -```json -{ - iss: "did:example:bob", - aud: "did:example:alice", - act: "msg/send", - nnc: "", - arg: { - from: "mailto:alice@example.com", - to: [ - "bob@example.com", - "carol@example.com" - ], - subject: "hello", - body: "world" - } - meta: { - fuel: 100, - disk: "100GB" - }, - prf: [bafy1, bafy2] -} - - - - - - - -``` js -{ - iss: "did:example:bob", - aud: "did:example:alice", // Origoinally removed these because it's duplicated from prf, but important for e.g. CRDT - run: { - sub: "did:example:alice", // <- where, IFF the subject is relevant... only really useful for - cmd: "counter/inc", - arg: {by: 4} - }, - meta: {}, - cause: {"/": "bafy...123"}, - prf: [bafy1, bafy3], - exp: 999999 // Doubles as a handy timeout -} -``` - -``` js -{ - iss: "did:example:bob", - aud: "did:example:alice", // NOTE: can be ANYONE in the delegation chain for proxying if you don't have a direct path? - exp: 999999, - run: { - act: "wasm/run", - arg: { - mod: "ipfs://...", - fun: "add_one", - arg: [42] - } - }, - prf: [bafy1, bafy3] -} -``` - - -NOTE TO SELF: keep aud field as optional. i.e. make it salient for ergonomic reasons / push it into the task writer's face. also aud & sub MAY diverge in the future. - ## 3.1 Fields | Name | Field | Type | Required | Notes | @@ -281,12 +228,6 @@ NOTE TO SELF: keep aud field as optional. i.e. make it salient for ergonomic rea | [Arguments] | `arg` | Map `{String: Any}` | Yes | | | [Nonce] | `nnc` | String | Yes | May be empty | -### 3.1.1 Subject - -The OPTIONAL `sub` field is intended for cases where parametrizing a specific agent is importnat - - - ### 3.1.3 Command The Command (`cmd`) field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a [UCAN Ability], which includes heirarchy, an Operation MUST be fully concrete. @@ -299,11 +240,15 @@ UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type If the `input` field is not present, it is implicitly a `unit` represented as empty map. -### 3.1.6 Nonce +### 3.1.1 Subject -If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in ASCII. This field ensures that multiple invocations are unique. The nonce MUST be left blank for commands that are expected to be idempotent. +The OPTIONAL `sub` field is intended for cases where parametrizing a specific agent is importnat + + + +### 3.1.6 Nonce -FIXME better wording +If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in ASCII. This field ensures that multiple (non-idempotent) invocations are unique. The nonce MUST be `""` for commands that are idempotent. ### 3.2.1 Interacting with an HTTP API @@ -411,7 +356,7 @@ FIXME expand to move proofs here -### 5.1.4 Optional Cause +### 5.1.4 Cause [Task]s MAY be invoked as an effect caused by a prior [Invocation]. Such [Invocation]s SHOULD have a `cause` field set to the [Receipt] link of the [Invocation] that caused it. The linked [Receipt] MUST have an `Effect` (the `fx` field) containing invoked [Task] in the `run` field. @@ -665,3 +610,91 @@ Thanks to [Christine Lemmer-Webber] for the many conversations about capability [eRights]: https:/erights.org [promise pipelining]: http://erights.org/elib/distrib/pipeline.html [ucanto RPC]: https://github.com/web3-storage/ucanto + + + + + + + + + + + + + + + + + + + + + + + + + +```json +{ + iss: "did:example:bob", + aud: "did:example:alice", + act: "msg/send", + nnc: "", + arg: { + from: "mailto:alice@example.com", + to: [ + "bob@example.com", + "carol@example.com" + ], + subject: "hello", + body: "world" + } + meta: { + fuel: 100, + disk: "100GB" + }, + prf: [bafy1, bafy2] +} + + + + + + + +``` js +{ + iss: "did:example:bob", + aud: "did:example:alice", // Origoinally removed these because it's duplicated from prf, but important for e.g. CRDT + run: { + sub: "did:example:alice", // <- where, IFF the subject is relevant... only really useful for + cmd: "counter/inc", + arg: {by: 4} + }, + meta: {}, + cause: {"/": "bafy...123"}, + prf: [bafy1, bafy3], + exp: 999999 // Doubles as a handy timeout +} +``` + +``` js +{ + iss: "did:example:bob", + aud: "did:example:alice", // NOTE: can be ANYONE in the delegation chain for proxying if you don't have a direct path? + exp: 999999, + run: { + act: "wasm/run", + arg: { + mod: "ipfs://...", + fun: "add_one", + arg: [42] + } + }, + prf: [bafy1, bafy3] +} +``` + + +NOTE TO SELF: keep aud field as optional. i.e. make it salient for ergonomic reasons / push it into the task writer's face. also aud & sub MAY diverge in the future. diff --git a/invocation.ipldsch b/invocation.ipldsch index 8f64b2c9..284d473e 100644 --- a/invocation.ipldsch +++ b/invocation.ipldsch @@ -1,9 +1,9 @@ -type SignedInvocation struct { - pld &Invocation +type Invocation struct { + pld &InvocationPayload sig &Signature } -type Invocation struct { +type InvocationPayload struct { uiv SemVer iss DID diff --git a/receipt.ipldsch b/receipt.ipldsch index fd07007d..0565e88e 100644 --- a/receipt.ipldsch +++ b/receipt.ipldsch @@ -1,18 +1,23 @@ -type SignedReceipt { +type Receipt { pld &Receipt sig Signature } -type Receipt struct { +type ReceiptPayload struct { iss DID ran &Invocation enq [&Task] - out Output + out Result iat Integer mta {String : Any} prf [&Delegation] - rec &SignedReceipt + rec &Receipt } + +type Result union { + | any "ok" + | any "error" +} representation keyed From 6d308e11d877d30eddfc79766533c036249850da Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 13:11:59 -0700 Subject: [PATCH 043/127] Fixup diuagrams and start working inside-out --- README.md | 184 +++++++++++++++++++-------------------------- invocation.ipldsch | 4 +- receipt.ipldsch | 2 +- 3 files changed, 82 insertions(+), 108 deletions(-) diff --git a/README.md b/README.md index ef7fe343..4eb21505 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,10 @@ # UCAN Invocation Specification v1.0.0-rc. 1 -FIXME FIXME show proxy invocation FIXME show delegated execution (work stealing) example -- move pipelines, await, etc to own spec -- explain in FAQ why we don't need `join` (i.e. use promises, since the model is inverted) -- operation -> command, Instruction -> action -- Be clear that you MUST sign +FIXME move pipelines, await, etc to own spec +FIXME explain in FAQ why we don't need `join` (i.e. use promises, since the model is inverted) +FIXME reverse lookup secion ## Editors @@ -154,35 +152,55 @@ The executor MUST be the UCAN delegate. Their DID MUST be set the in `aud` field | Concept | Description | |--------------|-----------------------------------------------------------------------------| -| [Invocation] | A request to perform some action based on [delegated][Delegation] authority | | [Command] | (Deferred) function application; a description of work to be performed | | [Task] | Contextual information for a [Command], such as resource limits | -| [Receipt] | The return value from an [Invocation], which may [enqueue] more tasks | +| [Invocation] | A request to perform some [Task] based on [delegated][Delegation] authority | | [Result] | The success value or error information from the run [Invocation] | +| [Receipt] | The return value from an [Invocation], which may [enqueue] more tasks | ``` mermaid flowchart RL - subgraph InvocationPayload - subgraph Invocation + subgraph Invocation + direction LR + subgraph InvocationPayload subgraph Task + direction TB + Command - DelegationProofs + DelegationProofs["Delegation Proofs"] end end - Signature + Inv1Sig end subgraph Receipt subgraph ReceiptPayload Ran Result + Enqueue end - Signature + RecSig[Signature] end - Ran -->|references| Invocation + subgraph Invocation2["(Next Invocation)"] + direction RL + + subgraph InvocationPayload2["Invocation Payload"] + subgraph Task2 + Command2["Command"] + DelegationProofs2["Delegation Proofs"] + end + + Cause + end + + Inv2Sig[Signature] + end + + Cause --> Enqueue + Ran --> Invocation ``` # 3 Command @@ -240,15 +258,16 @@ UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type If the `input` field is not present, it is implicitly a `unit` represented as empty map. -### 3.1.1 Subject +### 3.1.5 Subject -The OPTIONAL `sub` field is intended for cases where parametrizing a specific agent is importnat +The OPTIONAL `sub` field is intended for cases where parametrizing a specific agent is important. This is especially critical for two parts of the lifecycle: - +1. Specifying a particular `sub` (and thus `aud`) when [enqueuing new Tasks][enqueue] in a Receipt +2. Indexing Receipts for reverse lookup and memoization ### 3.1.6 Nonce -If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in ASCII. This field ensures that multiple (non-idempotent) invocations are unique. The nonce MUST be `""` for commands that are idempotent. +If present, the REQUIRED `nnc` field MUST include a random nonce expressed in ASCII. This field ensures that multiple (non-idempotent) invocations are unique. The nonce SHOULD be `""` for Commands that are idempotent. ### 3.2.1 Interacting with an HTTP API @@ -297,6 +316,8 @@ If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in AS ### 3.2.3 Running WebAssembly +In this case, the Wasm module is inlined. + ```json { "nnc": "", // FIXME infers idempotence @@ -319,81 +340,31 @@ Concretely, this means that the `&Task` MUST be present in the associated `auth` ## 4.1 Task -``` -type Task struct { - run &Command - meta {String : Any} - cause optional &Receipt -} -``` - -### 5.1.1 Task - -The `run` field MUST contain a link to the [Task] to be run. - -### 5.1.2 Metadata - -The OPTIONAL `meta` field MAY be used to include human-readable descriptions, tags, execution hints, resource limits, and so on. If present, the `meta` field MUST contain a map with string keys. The contents of the map are left undefined to encourage extensible use. - -If `meta` field is not present, it is implicitly a `unit` represented as an empty map. - -### 5.1.3 Proofs - -The `prf` field MUST contain links to any UCANs that provide the authority to perform this task. All of the outermost proofs MUST have `aud` field set to the [Executor]'s DID. All of the outmost proofs MUST have `iss` field set to the [Invoker]'s DID. - - - - +Tasks wrap a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. +| Field | Type | Description | Required | +|-------|---------------------------|----------------------------------------------------------------------------------------------|----------| +| `run` | `&Command` | The `run` field MUST contain a link to the [Task] to be run | Yes | +| `mta` | `{String : Any}` | Extensible fields, e.g. resource limits, human-readable tags, notes, and so on | Yes | +| `prf` | `[&Delegation]` | Links to any [UCAN Delegation]s that provide the authority to perform the enclosed [Command] | Yes | +| `exp` | `Integer | null`[^js-num] | The UTC Unix timestamp at which the Task expires | Yes | - -FIXME expand to move proofs here - - - - - - - - -### 5.1.4 Cause - -[Task]s MAY be invoked as an effect caused by a prior [Invocation]. Such [Invocation]s SHOULD have a `cause` field set to the [Receipt] link of the [Invocation] that caused it. The linked [Receipt] MUST have an `Effect` (the `fx` field) containing invoked [Task] in the `run` field. - -### 5.2.1 Task - -The [Task] containing the [Instruction] and any configuration. +# 5 Invocation # 6 Result -A Result records the output of the [Task], as well as its success or failure state. +A Result records the output of the [Task], as well as its success or failure state. A Result MUST be formatted as map with a single `tag` field. -## 6.1 Schema +| Tag | Value | Description | +|---------|------------------|-------------------------------------------------| +| `ok` | `Any` | The successfully returned data from a [Command] | +| `error` | `{String : Any}` | Error information from a failed [Command] | -``` -type Result union { - | any "ok" - | any "error" -} representation keyed -``` - -## 6.2 Variants - -## 6.2.1 Success - -The success branch MUST contain the value returned from a successful [Task] wrapped in the `"ok"` tag. The exact shape of the returned data is left undefined to allow for flexibility in various Task types. - -```json +```js +// Success { "ok": 42 } -``` - -## 6.2.2 Failure - -The failure branch MAY contain detail about why execution failed wrapped in the "error" tag. It is left undefined in this specification to allow for [Task] types to standardize the data that makes sense in their contexts. - -If no information is available, this field SHOULD be set to `{}`. -```json +// Failure { "error": { "dev/reason": "unauthorized", @@ -402,28 +373,6 @@ If no information is available, this field SHOULD be set to `{}`. } ``` -## 7 Effects - -The result of an [Invocation] MAY include a request for further actions to be performed via "effects". This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. - -Effects describe requests for future work to be performed. All [Invocation]s in an [Effect] block MUST be treated as concurrent, unless explicit data dependencies between them exist via promise [Pipeline]s. The `fx` block contains two fields: `fork` and `join`. - -[Task]s listed in the `fork` field are first-class and only ordered by promises; they otherwise SHOULD be considered independent and equal. As such, atomic guarantees such as failure of one effect implying failure of other effects if left undefined. - -The `join` field describes an OPTIONAL "special" [Invocation] which instruct the [Executor] that the [Task] [Invocation] is a continuation of the previous Invocation. This roughly emulates a virtual thread which terminates in an Invocation that produces Effect without a `join` field. - -Tasks in the `fork` field MAY be related to the Task in the `join` field if there exists a Promise referencing either Task. If such a promise does not exist, then they SHOULD be treated as entirely separate and MAY be scheduled, deferred, fail, retry, and so on entirely separately. - -## 7.1 Schema - -``` -# Represents a request to invoke enclosed set of tasks concurrently -type Effects { - fx [&Invocation] -} -``` - -## 7.2 Fields # 8 Receipt @@ -531,6 +480,31 @@ Receipts MUST use the same version as the invocation that they contain. ``` +## 7 Effects + +The result of an [Invocation] MAY include a request for further actions to be performed via "effects". This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. + +Effects describe requests for future work to be performed. All [Invocation]s in an [Effect] block MUST be treated as concurrent, unless explicit data dependencies between them exist via promise [Pipeline]s. The `fx` block contains two fields: `fork` and `join`. + +[Task]s listed in the `fork` field are first-class and only ordered by promises; they otherwise SHOULD be considered independent and equal. As such, atomic guarantees such as failure of one effect implying failure of other effects if left undefined. + +The `join` field describes an OPTIONAL "special" [Invocation] which instruct the [Executor] that the [Task] [Invocation] is a continuation of the previous Invocation. This roughly emulates a virtual thread which terminates in an Invocation that produces Effect without a `join` field. + +Tasks in the `fork` field MAY be related to the Task in the `join` field if there exists a Promise referencing either Task. If such a promise does not exist, then they SHOULD be treated as entirely separate and MAY be scheduled, deferred, fail, retry, and so on entirely separately. + +## 7.1 Schema + +``` +# Represents a request to invoke enclosed set of tasks concurrently +type Effects { + fx [&Invocation] +} +``` + +## 7.2 Fields + + + diff --git a/invocation.ipldsch b/invocation.ipldsch index 284d473e..11f9e606 100644 --- a/invocation.ipldsch +++ b/invocation.ipldsch @@ -1,5 +1,5 @@ type Invocation struct { - pld &InvocationPayload + inv &InvocationPayload sig &Signature } @@ -17,7 +17,7 @@ type Task struct { run &Command mta {String : Any} prf [&Delegation] - exp Integer + exp optional Integer } type Action { diff --git a/receipt.ipldsch b/receipt.ipldsch index 0565e88e..61a5f9ca 100644 --- a/receipt.ipldsch +++ b/receipt.ipldsch @@ -1,6 +1,6 @@ type Receipt { pld &Receipt - sig Signature + sig &Signature } type ReceiptPayload struct { From 87fa376cc78007549c383b301aa4ff8a663c8de2 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 15:00:18 -0700 Subject: [PATCH 044/127] Save point! ERD --- README.md | 255 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 171 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 4eb21505..9721bc73 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,18 @@ flowchart RL Ran --> Invocation ``` -# 3 Command +## 2.3 Lifecycle + +``` mermaid +erDiagram + Invocation }|--|| Task: requests + Invocation ||--|| Receipt: returns + Receipt |o--|{ Task: enqueues +``` + +# 3 Request + +## 3.1 Command A Command is the smallest unit of work that can be Invoked. It is akin to a function call or actor message. @@ -237,20 +248,20 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to }) ``` -## 3.1 Fields +### 3.1.1 Fields -| Name | Field | Type | Required | Notes | -|-------------|-------|---------------------|----------|--------------| -| [Subject] | `sub` | DID | No | | -| [Command] | `cmd` | String | Yes | | -| [Arguments] | `arg` | Map `{String: Any}` | Yes | | -| [Nonce] | `nnc` | String | Yes | May be empty | +| Name | Field | Type | Required | +|-------------|-------|---------------------|----------| +| [Subject] | `sub` | DID | No | +| [Command] | `cmd` | String | Yes | +| [Arguments] | `arg` | Map `{String: Any}` | Yes | +| [Nonce] | `nnc` | String | Yes | -### 3.1.3 Command +#### 3.1.1.1 Command The Command (`cmd`) field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a [UCAN Ability], which includes heirarchy, an Operation MUST be fully concrete. -### 3.1.4 Arguments +#### 3.1.1.2 Arguments The Arguments (`arg`) field, MAY contain any parameters expected by the Resource/Operation pair, which MAY be different between different Resources and Operations, and is thus left to the executor to define the shape of this data. This field MUST be representable as a map or keyword list. @@ -258,18 +269,20 @@ UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type If the `input` field is not present, it is implicitly a `unit` represented as empty map. -### 3.1.5 Subject +#### 3.1.1.3 Subject The OPTIONAL `sub` field is intended for cases where parametrizing a specific agent is important. This is especially critical for two parts of the lifecycle: 1. Specifying a particular `sub` (and thus `aud`) when [enqueuing new Tasks][enqueue] in a Receipt 2. Indexing Receipts for reverse lookup and memoization -### 3.1.6 Nonce +#### 3.1.1.4 Nonce If present, the REQUIRED `nnc` field MUST include a random nonce expressed in ASCII. This field ensures that multiple (non-idempotent) invocations are unique. The nonce SHOULD be `""` for Commands that are idempotent. -### 3.2.1 Interacting with an HTTP API +### 3.1.2 Exmaples + +Interacting with an HTTP API ```json { @@ -296,7 +309,7 @@ If present, the REQUIRED `nnc` field MUST include a random nonce expressed in AS } ``` -### 3.2.2 Sending Email +Sending Email: ```json { @@ -314,9 +327,7 @@ If present, the REQUIRED `nnc` field MUST include a random nonce expressed in AS } ``` -### 3.2.3 Running WebAssembly - -In this case, the Wasm module is inlined. +Running WebAssembly. In this case, the Wasm module is inlined. ```json { @@ -330,7 +341,7 @@ In this case, the Wasm module is inlined. } ``` -# 4 Task +## 3.2 Task As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is an instruction to the [Executor] to perform enclosed [Task]. [Invocation]s are not executable until they have been provided provable authority (in form of UCANs in the `prf` field) and an [Authorization] (in the `auth` field) from the [Invoker]. @@ -338,8 +349,6 @@ The `auth` field MUST be contain an [Authorization] which signs over the `&Task` Concretely, this means that the `&Task` MUST be present in the associated `auth`'s `scope` field. A `Receipt` where the associated [Authorization] does not include the [Task] in the `scope` MUST be considered invalid. -## 4.1 Task - Tasks wrap a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. | Field | Type | Description | Required | @@ -349,9 +358,21 @@ Tasks wrap a [Command] with contextual information. This includes expiration tim | `prf` | `[&Delegation]` | Links to any [UCAN Delegation]s that provide the authority to perform the enclosed [Command] | Yes | | `exp` | `Integer | null`[^js-num] | The UTC Unix timestamp at which the Task expires | Yes | -# 5 Invocation +## 3.3 Invocation -# 6 Result +The Invocation attaches the authentication information required to authorize the [Task]. It consists of an InvocationPayload, and a signture envelope. + +### 3.3.1 InvocationPayload + +### 3.3.2 Invocation (Envelope) + +| Field | Type | Required | Description | +|------|-------|--------|-------------| +| `uci` | `InvocationPayload` | Yes | + +# 4 Response + +## 4.1 Result A Result records the output of the [Task], as well as its success or failure state. A Result MUST be formatted as map with a single `tag` field. @@ -373,9 +394,7 @@ A Result records the output of the [Task], as well as its success or failure sta } ``` - - -# 8 Receipt +# 4.2 Receipt A `Receipt` is an attestation of the [Result] and requested [Effect]s by a [Task] [Invocation]. A Receipt MUST be signed by the [Executor] or it's delegate. If signed by the delegate, the proof of delegation from the [Executor] to the delegate (the `iss` of the receipt) MUST be provided in `prf`. @@ -385,17 +404,91 @@ Receipts MUST use the same version as the invocation that they contain. ## 8.1 Receipt -| Field | Type | Required | Description | -|-------|------------------|----------|------------------------------------------------------------------------------------| -| `iss` | `DID` | Yes | The DID of the Executor | -| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | -| `ran` | `&Invocation` | Yes | MUST be a link to the [Invocation] that the Receipt is for | -| `out` | `Result` | Yes | MUST contain the value output of the invocation in [Result] format | -| `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | -| `mta` | `{String : Any}` | Yes | Additional data about the receipt | -| `rec` | `&Receipt` | No | Recursive `Signed`s if the Invocation was proxied to another Executor | +### 8.1.1 Receipt Payload + +| Field | Type | Required | Description | +|-------|--------------------|----------|------------------------------------------------------------------------------------| +| `iss` | `DID` | Yes | The DID of the Executor | +| `ran` | `&Invocation` | Yes | MUST be a link to the [Invocation] that the Receipt is for | +| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | +| `out` | `Result` | Yes | MUST contain the value output of the invocation in [Result] format | +| `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | +| `mta` | `{String : Any}` | Yes | Additional data about the receipt | +| `rec` | `&Receipt` | No | Recursive `Signed`s if the Invocation was proxied to another Executor | +| `iat` | `Integer`[^js-num] | No | The UTC Unix timestand at which the Receipt was issed | + +A couple of these fields warrant further comment below. -## 8.3 DAG-JSON Examples +#### 8.1.1.1 Proof + +If the Receipt Issuer is not identical to the `aud` field of Invocation referenced in the `ran` field, a [Delegation] proof chain SHOULD be included. If a chain is present, it MUST show that + +``` js +// Pseudocode + +const delegation = { + iss: "did:web:example.com", + aud: "did:plc:ewvi7nxzyoun6zhxrhs64oiz", + can: "crud/update", + // ... +} + +const workerDelegation = { + iss: "did:web:example.com", + aud: "did:web:worker.not-example.net" + sub: "did:web:example.com", + can: "ucan/execute", + // ... +} + +const invocation = { + iss: "did:plc:ewvi7nxzyoun6zhxrhs64oiz", + aud: "did:web:example.com", + prf: [ cid(delegation) ], + // ... +} + +const receipt = { + iss: "did:web:worker.not-example.net", + ran: cid(invocation) + prf: [ cid(workerDelegation) ], + // ... +} +``` + +For u + +#### 8.1.1.2 Enqueue + +The result of an [Invocation] MAY include a request for further actions to be performed via "effects". This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. + +Enqueued [Task]s describe requests for future work to be performed. The SHOULD come with [Delegation]s + +All [Invocation]s in an [Effect] block MUST be treated as concurrent, unless explicit data dependencies between them exist via promise [Pipeline]s. The `fx` block contains two fields: `fork` and `join`. + +[Task]s listed in the `fork` field are first-class and only ordered by promises; they otherwise SHOULD be considered independent and equal. As such, atomic guarantees such as failure of one effect implying failure of other effects if left undefined. + +The `join` field describes an OPTIONAL "special" [Invocation] which instruct the [Executor] that the [Task] [Invocation] is a continuation of the previous Invocation. This roughly emulates a virtual thread which terminates in an Invocation that produces Effect without a `join` field. + +Tasks in the `fork` field MAY be related to the Task in the `join` field if there exists a Promise referencing either Task. If such a promise does not exist, then they SHOULD be treated as entirely separate and MAY be scheduled, deferred, fail, retry, and so on entirely separately. + +## 7.1 Schema + +``` +# Represents a request to invoke enclosed set of tasks concurrently +type Effects { + fx [&Invocation] +} +``` + +### 8.1.2 Receipt Envelope + +| Field | Type | Required | Description | +|-------|-------------------|----------|--------------------------------------------------------------------------| +| `uci` | `&ReceiptPayload` | Yes | The CID for the [Receipt Payload] | +| `sig` | `Signature` | Yes | The [Signature] of the `uci` value, by the [Receipt Payload]'s `iss` DID | + +### 8.1.3 DAG-JSON Examples ### 8.3.1 Issued by Executor @@ -478,55 +571,6 @@ Receipts MUST use the same version as the invocation that they contain. "sig": { "/": { "bytes": "7aEDQAHWabtCE+QikM3Np94TrA5T8n2yXqy8Uf35hgw0fe5c2Xi1O0h/JgrFmGl2Gsbhfm05zpdQmwfK2f/Sbe00YQE" } } } ``` - - -## 7 Effects - -The result of an [Invocation] MAY include a request for further actions to be performed via "effects". This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. - -Effects describe requests for future work to be performed. All [Invocation]s in an [Effect] block MUST be treated as concurrent, unless explicit data dependencies between them exist via promise [Pipeline]s. The `fx` block contains two fields: `fork` and `join`. - -[Task]s listed in the `fork` field are first-class and only ordered by promises; they otherwise SHOULD be considered independent and equal. As such, atomic guarantees such as failure of one effect implying failure of other effects if left undefined. - -The `join` field describes an OPTIONAL "special" [Invocation] which instruct the [Executor] that the [Task] [Invocation] is a continuation of the previous Invocation. This roughly emulates a virtual thread which terminates in an Invocation that produces Effect without a `join` field. - -Tasks in the `fork` field MAY be related to the Task in the `join` field if there exists a Promise referencing either Task. If such a promise does not exist, then they SHOULD be treated as entirely separate and MAY be scheduled, deferred, fail, retry, and so on entirely separately. - -## 7.1 Schema - -``` -# Represents a request to invoke enclosed set of tasks concurrently -type Effects { - fx [&Invocation] -} -``` - -## 7.2 Fields - - - - - - - - - - - - - - - - - - - - - - - - - # 10 Prior Art @@ -672,3 +716,46 @@ Thanks to [Christine Lemmer-Webber] for the many conversations about capability NOTE TO SELF: keep aud field as optional. i.e. make it salient for ergonomic reasons / push it into the task writer's face. also aud & sub MAY diverge in the future. + + + + + +NOTE: can make great use of batrch signatures +NOTE TO SELF: batch signatures need trees, too? + +``` mermaid +sequenceDiagram + actor User + + participant Service + + participant WorkQueue + participant Worker1 + participant Worker2 + + autonumber + + Note over Service, Worker2: Service Setup + WorkQueue -->> Service: delegate(WorkQueue, queue/push) + Service -->> WorkQueue: delegate(Service, ucan/proxy/sign) + + WorkQueue -->> Worker1: delegate(WorkQueue, queue/pop) + WorkQueue -->> Worker1: delegate(Service, ucan/proxy/sign) + + WorkQueue -->> Worker2: delegate(WorkQueue, queue/pop) + WorkQueue -->> Worker2: delegate(Service, ucan/proxy/sign) + + Note over User, Service: Delegates to User + Service -->> User: delegate(Service, crud/update) + + Note over User, Worker2: Invocation with Proxy Execution + User ->> Service: invoke(Service, [crud/update, "foo", 42], prf: [➐]) + Service -) WorkQueue: invoke(WorkQueue, queue/push, [➑], prf: [➊]) + + Note over WorkQueue, Worker2: Work Stealing + Worker2 ->>+ WorkQueue: invoke(WorkQueue, queue/pop, prf: [➎]) + WorkQueue ->>- Worker2: receipt(inv: ➓, out: ➒) + Worker2 ->> Worker2: Execute!(➒) + Worker2 -) User: receipt(out: ok, inv: ➒, prf: [➏,➋]) +``` From dee01119eb100d08a6cffade45277ec6f9acebdf Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 15:18:07 -0700 Subject: [PATCH 045/127] More clarifyinb information --- README.md | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9721bc73..04126233 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,23 @@ The executor is directed to perform some task described in the UCAN by the invok The executor MUST be the UCAN delegate. Their DID MUST be set the in `aud` field of the contained UCAN. -## 2.2 Anatomy +## 2.2 Lifecycle + +At a very high level: + +- A [Task] absractly requests some action +- An [Invocation] attaches proven ([Delegation]) authority to a [Task] +- A [Receipt] MAY request that the Invoker enqueue more [Task]s + +``` mermaid +erDiagram + Delegation }o--|{ Invocation: proves + Invocation }|--|| Task: requests + Invocation ||--|| Receipt: returns + Receipt |o--|{ Task: enqueues +``` + +## 2.3 Anatomy | Concept | Description | |--------------|-----------------------------------------------------------------------------| @@ -199,17 +215,8 @@ flowchart RL Inv2Sig[Signature] end - Cause --> Enqueue - Ran --> Invocation -``` - -## 2.3 Lifecycle - -``` mermaid -erDiagram - Invocation }|--|| Task: requests - Invocation ||--|| Receipt: returns - Receipt |o--|{ Task: enqueues + Cause -->|provenance| Enqueue + Ran -->|provenance| Invocation ``` # 3 Request From 1482effcc2fa84ab8b163d2eb26ac69738adeb8c Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 15:24:54 -0700 Subject: [PATCH 046/127] Remove section on pipelining ahead of breaking out that spec --- README.md | 90 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 04126233..a2e8dd2b 100644 --- a/README.md +++ b/README.md @@ -79,49 +79,11 @@ For example, if `alice@example.com` delegates her `web3.storage` storage quota t By distinguishing invocation from delegation, agents are able to understand the user intention, and handle such messages accordingly. Receipt of an invocation with misaligned principles will fail, but a delegation may be held in e.g. Bob's proxy inbox to be acted on when he comes online or widely distributed across the `web3.storage` infrastructure. -## 1.3 Separation of Concerns - -Information about the scheduling, order, and pipelining of tasks is orthogonal to the flow of authority. An agent collaborating with the original executor does not need to know that their call is 3 invocations deep; they only need to know that they been asked to perform some task by the latest invoker. - -As we shall see in the [discussion of promise pipelining][Pipeline], asking an agent to perform a sequence of tasks before you know the exact parameters requires delegating capabilities for all possible steps in the pipeline. Pulling pipelining detail out of the core UCAN spec serves two functions: it keeps the UCAN spec focused on the flow of authority, and makes salient the level of de facto authority that the executor has (since they can claim any value as having returned for any step). - -``` mermaid -sequenceDiagram - participant Alice 💾 - participant Bob - participant Carol 📧 - participant Dan - - autonumber - - Note over Alice 💾, Dan: Delegations - Alice 💾 -->> Bob: Delegate - Bob -->> Carol 📧: Delegate - Carol 📧 -->> Dan: Delegate - Carol 📧 -->> Dan: Delegate - - Note over Alice 💾, Dan: Single Invocation - Dan ->> Alice 💾: Read from Alice's DB! - Alice 💾 -->> Dan: Result<➎> - - Note over Alice 💾, Dan: Multiple Invocation Flow - Dan ->> Alice 💾: Read from Alice's DB! - Alice 💾 -->> Dan: Result<➐> - Dan ->> Carol 📧: Send email containing Result<➐> as Carol! - Carol 📧 ->> Carol 📧: Send email! - - Note over Alice 💾, Dan: Promise Pipeline - Dan ->> Alice 💾: Read from Alice's DB! - Dan ->> Carol 📧: Send email containing Result<⓫> as Carol! - Alice 💾 -->> Carol 📧: Result<⓫> - Carol 📧 ->> Carol 📧: Send email containing Result<⓫> as Carol! -``` - -## 1.4 Serialization +## 1.3 Serialization Unlike [UCAN Delegation]s, Invocations are point-to-point. This means that — aside from an Instruction ID — the exact format need only be agreed on by the two parties directly communicating. The examples in this document are given as [JWT], but others (such as [`invocation-ipld`]) MAY be used as long as it's accepted by both parties. -## 1.5 Public Resources +## 1.4 Public Resources A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard required for initiating flow (e.g. signup). These cases typically involve a reference passed out of band (such as a web link). Due to [designation without authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain , but this SHOULD NOT be the default behaviour. @@ -766,3 +728,51 @@ sequenceDiagram Worker2 ->> Worker2: Execute!(➒) Worker2 -) User: receipt(out: ok, inv: ➒, prf: [➏,➋]) ``` + + + + + + + + + + + + + + + + +> Bob: Delegate + Bob -->> Carol 📧: Delegate + Carol 📧 -->> Dan: Delegate + Carol 📧 -->> Dan: Delegate + + Note over Alice 💾, Dan: Single Invocation + Dan ->> Alice 💾: Read from Alice's DB! + Alice 💾 -->> Dan: Result<➎> + + Note over Alice 💾, Dan: Multiple Invocation Flow + Dan ->> Alice 💾: Read from Alice's DB! + Alice 💾 -->> Dan: Result<➐> + Dan ->> Carol 📧: Send email containing Result<➐> as Carol! + Carol 📧 ->> Carol 📧: Send email! + + Note over Alice 💾, Dan: Promise Pipeline + Dan ->> Alice 💾: Read from Alice's DB! + Dan ->> Carol 📧: Send email containing Result<⓫> as Carol! + Alice 💾 -->> Carol 📧: Result<⓫> + Carol 📧 ->> Carol 📧: Send email containing Result<⓫> as Carol! +--> From 67bf51d39f0444583bbe582b64b6428fd9de2684 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 15:30:50 -0700 Subject: [PATCH 047/127] Note on Promises --- README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a2e8dd2b..b19017f4 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,10 @@ Unlike [UCAN Delegation]s, Invocations are point-to-point. This means that — a A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard required for initiating flow (e.g. signup). These cases typically involve a reference passed out of band (such as a web link). Due to [designation without authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain , but this SHOULD NOT be the default behaviour. +## 1.5 Promise Pipelining + +[UCAN Promise] extends UCAN Invocation with [distributed promise pipelines]. Promises are helpful in a wide variety of situations for efficiency and convenience. Implementing [UCAN Promise]s is RECOMMENDED. + # 2 Concepts ## 2.1 Roles @@ -145,11 +149,11 @@ flowchart RL direction TB Command - DelegationProofs["Delegation Proofs"] + DelegationProofs[Delegation Proofs] end end - Inv1Sig + Inv1Sig[Signature] end subgraph Receipt @@ -162,13 +166,13 @@ flowchart RL RecSig[Signature] end - subgraph Invocation2["(Next Invocation)"] + subgraph Invocation2["(Next) Invocation"] direction RL - subgraph InvocationPayload2["Invocation Payload"] + subgraph InvocationPayload2[Invocation Payload] subgraph Task2 Command2["Command"] - DelegationProofs2["Delegation Proofs"] + DelegationProofs2[Delegation Proofs] end Cause @@ -589,13 +593,14 @@ Thanks to [Christine Lemmer-Webber] for the many conversations about capability [Simon Worthington]: https://github.com/simonwo [UCAN Ability]: https://github.com/ucan-wg/delegation/#23-ability [UCAN Delegation]: https://github.com/ucan-wg/delegation/ +[UCAN Promise]: https://github.com/ucan-wg/promise/ [URI]: https://en.wikipedia.org/wiki/Uniform_Resource_Identifier [Zeeshan Lakhani]: https://github.com/zeeshanlakhani [`data`]: https://en.wikipedia.org/wiki/Data_URI_scheme [`ipfs`]: https://docs.ipfs.tech/how-to/address-ipfs-on-web/#native-urls [`magnet`]: https://en.wikipedia.org/wiki/Magnet_URI_scheme [eRights]: https:/erights.org -[promise pipelining]: http://erights.org/elib/distrib/pipeline.html +[promise pipelines]: http://erights.org/elib/distrib/pipeline.html [ucanto RPC]: https://github.com/web3-storage/ucanto @@ -619,7 +624,6 @@ Thanks to [Christine Lemmer-Webber] for the many conversations about capability - ```json From b17c39baf19fce50492598b80015a2aa823c0828 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 15:31:24 -0700 Subject: [PATCH 048/127] Remove note on serialziation --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b19017f4..b050362d 100644 --- a/README.md +++ b/README.md @@ -79,15 +79,11 @@ For example, if `alice@example.com` delegates her `web3.storage` storage quota t By distinguishing invocation from delegation, agents are able to understand the user intention, and handle such messages accordingly. Receipt of an invocation with misaligned principles will fail, but a delegation may be held in e.g. Bob's proxy inbox to be acted on when he comes online or widely distributed across the `web3.storage` infrastructure. -## 1.3 Serialization - -Unlike [UCAN Delegation]s, Invocations are point-to-point. This means that — aside from an Instruction ID — the exact format need only be agreed on by the two parties directly communicating. The examples in this document are given as [JWT], but others (such as [`invocation-ipld`]) MAY be used as long as it's accepted by both parties. - -## 1.4 Public Resources +## 1.3 Public Resources A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard required for initiating flow (e.g. signup). These cases typically involve a reference passed out of band (such as a web link). Due to [designation without authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain , but this SHOULD NOT be the default behaviour. -## 1.5 Promise Pipelining +## 1.4 Promise Pipelining [UCAN Promise] extends UCAN Invocation with [distributed promise pipelines]. Promises are helpful in a wide variety of situations for efficiency and convenience. Implementing [UCAN Promise]s is RECOMMENDED. From e8526d20e5eb53fe2dfface00af74e66d725c1d1 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 16:01:44 -0700 Subject: [PATCH 049/127] Annotate example --- README.md | 148 ++++++++++++++++++++++++--------------------- invocation.ipldsch | 2 +- 2 files changed, 81 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index b050362d..8affdc35 100644 --- a/README.md +++ b/README.md @@ -185,9 +185,16 @@ flowchart RL ## 3.1 Command -A Command is the smallest unit of work that can be Invoked. It is akin to a function call or actor message. +A Command is the smallest unit of work that can be Invoked. It is akin to a function call or actor message. It MUST conform to the following struct shape: -The `input` field is free form, and depend on the specific resource and ability being interacted with, and is not described in this specification. +| Name | Field | Type | Required | +|-------------|-------|---------------------|----------| +| [Subject] | `sub` | DID | No | +| [Command] | `cmd` | String | Yes | +| [Arguments] | `arg` | Map `{String: Any}` | Yes | +| [Nonce] | `nnc` | String | Yes | + +The `arg` field MUST be defined by the `cmd` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. Using the JavaScript analogy from the introduction, an Instruction is similar to wrapping a call in a closure: @@ -217,20 +224,11 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to }) ``` -### 3.1.1 Fields - -| Name | Field | Type | Required | -|-------------|-------|---------------------|----------| -| [Subject] | `sub` | DID | No | -| [Command] | `cmd` | String | Yes | -| [Arguments] | `arg` | Map `{String: Any}` | Yes | -| [Nonce] | `nnc` | String | Yes | - -#### 3.1.1.1 Command +### 3.1.1 Command The Command (`cmd`) field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a [UCAN Ability], which includes heirarchy, an Operation MUST be fully concrete. -#### 3.1.1.2 Arguments +### 3.1.2 Arguments The Arguments (`arg`) field, MAY contain any parameters expected by the Resource/Operation pair, which MAY be different between different Resources and Operations, and is thus left to the executor to define the shape of this data. This field MUST be representable as a map or keyword list. @@ -238,44 +236,86 @@ UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type If the `input` field is not present, it is implicitly a `unit` represented as empty map. -#### 3.1.1.3 Subject +### 3.1.3 Subject The OPTIONAL `sub` field is intended for cases where parametrizing a specific agent is important. This is especially critical for two parts of the lifecycle: 1. Specifying a particular `sub` (and thus `aud`) when [enqueuing new Tasks][enqueue] in a Receipt 2. Indexing Receipts for reverse lookup and memoization -#### 3.1.1.4 Nonce +### 3.1.4 Nonce If present, the REQUIRED `nnc` field MUST include a random nonce expressed in ASCII. This field ensures that multiple (non-idempotent) invocations are unique. The nonce SHOULD be `""` for Commands that are idempotent. -### 3.1.2 Exmaples +## 3.2 Task -Interacting with an HTTP API +As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is an instruction to the [Executor] to perform enclosed [Task]. [Invocation]s are not executable until they have been provided provable authority (in form of UCANs in the `prf` field) and an [Authorization] (in the `auth` field) from the [Invoker]. -```json -{ - // ... - run: { - "act": "crud/create", - "arg": { - "uri" "https://example.com/blog/posts", - "headers": { - "content-type": "application/json" - }, - "payload": { - "title": "How UCAN Tasks Changed My Life", - "body": "This is the story of how one spec changed everything...", - "topics": [ - "authz", - "journal" - ], - "draft": true - } - }, - "nnc": "&NCC-1701-D*" - } -} +The `auth` field MUST be contain an [Authorization] which signs over the `&Task` in `run`. + +Concretely, this means that the `&Task` MUST be present in the associated `auth`'s `scope` field. A `Receipt` where the associated [Authorization] does not include the [Task] in the `scope` MUST be considered invalid. + +Tasks wrap a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. + +| Field | Type | Description | Required | +|-------|---------------------------|----------------------------------------------------------------------------------------------|----------| +| `run` | `&Command` | The `run` field MUST contain a link to the [Task] to be run | Yes | +| `mta` | `{String : Any}` | Extensible fields, e.g. resource limits, human-readable tags, notes, and so on | Yes | +| `prf` | `[&Delegation]` | Links to any [UCAN Delegation]s that provide the authority to perform the enclosed [Command] | Yes | +| `exp` | `Integer | null`[^js-num] | The UTC Unix timestamp at which the Task expires | Yes | + +## 3.3 Invocation + +The Invocation attaches the authentication information required to authorize the [Task]. It consists of an InvocationPayload, and a signture envelope. + +### 3.3.1 InvocationPayload + +### 3.3.2 Invocation (Envelope) + +| Field | Type | Required | Description | +|------|-------|--------|-------------| +| `uci` | `InvocationPayload` | Yes | + +## 3.4 Examples + +### 3.4.1 Interacting with an HTTP API + +```js +{ // ┐ + sig: "FIXME", // │ + inv: { // ┐ │ + iss: "did:web:example.com", // │ │ + aud: "did:......FIXME", // │ │ + run: { // ┐ │ │ + act: { // ┐ │ │ │ + nnc: "&NCC-1701-D*", // │ │ │ │ + cmd: "crud/create", // │ │ │ │ + arg: { // │ │ │ │ + uri "https://example.com/blog/posts", // │ │ │ │ + headers: { // │ │ │ │ + content-type: "application/json" // │ │ │ │ + }, // ├── Action │ │ │ + payload: { // │ │ │ │ + title: "UCAN for Fun an Profit", // │ │ │ │ + body: "UCAN is great!", // │ ├── Task ├── Payload ├── Invocation + topics: ["authz", "journal"], // │ │ │ │ + draft": true // │ │ │ │ + } // │ │ │ │ + } // │ │ │ │ + }, // ┘ │ │ │ + mta: { // │ │ │ + env: "development", // │ │ │ + tags: ["blog", "post", "pr#123"] // │ │ │ + }, // │ │ │ + prf: [ // │ │ │ + {"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}, // │ │ │ + {"/": "bafkr4idnrqfouibxdqpvh2lmkhgsbw5yabvjbiaea3fplrb4vxifaphvgy"}, // │ │ │ + {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"}, // │ │ │ + ], // │ │ │ + exp: 1697409438 // │ │ │ + } // ┘ │ │ + } // ┘ │ +} // ┘ ``` Sending Email: @@ -310,34 +350,6 @@ Running WebAssembly. In this case, the Wasm module is inlined. } ``` -## 3.2 Task - -As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is an instruction to the [Executor] to perform enclosed [Task]. [Invocation]s are not executable until they have been provided provable authority (in form of UCANs in the `prf` field) and an [Authorization] (in the `auth` field) from the [Invoker]. - -The `auth` field MUST be contain an [Authorization] which signs over the `&Task` in `run`. - -Concretely, this means that the `&Task` MUST be present in the associated `auth`'s `scope` field. A `Receipt` where the associated [Authorization] does not include the [Task] in the `scope` MUST be considered invalid. - -Tasks wrap a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. - -| Field | Type | Description | Required | -|-------|---------------------------|----------------------------------------------------------------------------------------------|----------| -| `run` | `&Command` | The `run` field MUST contain a link to the [Task] to be run | Yes | -| `mta` | `{String : Any}` | Extensible fields, e.g. resource limits, human-readable tags, notes, and so on | Yes | -| `prf` | `[&Delegation]` | Links to any [UCAN Delegation]s that provide the authority to perform the enclosed [Command] | Yes | -| `exp` | `Integer | null`[^js-num] | The UTC Unix timestamp at which the Task expires | Yes | - -## 3.3 Invocation - -The Invocation attaches the authentication information required to authorize the [Task]. It consists of an InvocationPayload, and a signture envelope. - -### 3.3.1 InvocationPayload - -### 3.3.2 Invocation (Envelope) - -| Field | Type | Required | Description | -|------|-------|--------|-------------| -| `uci` | `InvocationPayload` | Yes | # 4 Response diff --git a/invocation.ipldsch b/invocation.ipldsch index 11f9e606..f680895d 100644 --- a/invocation.ipldsch +++ b/invocation.ipldsch @@ -14,7 +14,7 @@ type InvocationPayload struct { } type Task struct { - run &Command + act &Command mta {String : Any} prf [&Delegation] exp optional Integer From 0765989ec12b1fc99b1a14ea3c623eaf05e0972d Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 16:04:02 -0700 Subject: [PATCH 050/127] Fix length --- README.md | 70 +++++++++++++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 8affdc35..125ca7bf 100644 --- a/README.md +++ b/README.md @@ -281,41 +281,41 @@ The Invocation attaches the authentication information required to authorize the ### 3.4.1 Interacting with an HTTP API ```js -{ // ┐ - sig: "FIXME", // │ - inv: { // ┐ │ - iss: "did:web:example.com", // │ │ - aud: "did:......FIXME", // │ │ - run: { // ┐ │ │ - act: { // ┐ │ │ │ - nnc: "&NCC-1701-D*", // │ │ │ │ - cmd: "crud/create", // │ │ │ │ - arg: { // │ │ │ │ - uri "https://example.com/blog/posts", // │ │ │ │ - headers: { // │ │ │ │ - content-type: "application/json" // │ │ │ │ - }, // ├── Action │ │ │ - payload: { // │ │ │ │ - title: "UCAN for Fun an Profit", // │ │ │ │ - body: "UCAN is great!", // │ ├── Task ├── Payload ├── Invocation - topics: ["authz", "journal"], // │ │ │ │ - draft": true // │ │ │ │ - } // │ │ │ │ - } // │ │ │ │ - }, // ┘ │ │ │ - mta: { // │ │ │ - env: "development", // │ │ │ - tags: ["blog", "post", "pr#123"] // │ │ │ - }, // │ │ │ - prf: [ // │ │ │ - {"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}, // │ │ │ - {"/": "bafkr4idnrqfouibxdqpvh2lmkhgsbw5yabvjbiaea3fplrb4vxifaphvgy"}, // │ │ │ - {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"}, // │ │ │ - ], // │ │ │ - exp: 1697409438 // │ │ │ - } // ┘ │ │ - } // ┘ │ -} // ┘ +{ + sig: {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, + inv: { // ┐ + iss: "did:web:example.com", // │ + aud: "did:......FIXME", // │ + run: { // ┐ │ + act: { // ┐ │ │ + nnc: "&NCC-1701-D*", // │ │ │ + cmd: "crud/create", // │ │ │ + arg: { // │ │ │ + uri "https://example.com/blog/posts", // │ │ │ + headers: { // │ │ │ + content-type: "application/json" // │ │ │ + }, // ├── Action │ │ + payload: { // │ │ │ + title: "UCAN for Fun an Profit", // │ │ │ + body: "UCAN is great!", // │ ├── Task ├── Payload + topics: ["authz", "journal"], // │ │ │ + draft": true // │ │ │ + } // │ │ │ + } // │ │ │ + }, // ┘ │ │ + mta: { // │ │ + env: "development", // │ │ + tags: ["blog", "post", "pr#123"] // │ │ + }, // │ │ + prf: [ // │ │ + {"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}, // │ │ + {"/": "bafkr4idnrqfouibxdqpvh2lmkhgsbw5yabvjbiaea3fplrb4vxifaphvgy"}, // │ │ + {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"}, // │ │ + ], // │ │ + exp: 1697409438 // │ │ + } // ┘ │ + } // ┘ +} ``` Sending Email: From c4d1ca3263a9b12d00d2d4aa64bc8f6ca96fe080 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 16:07:28 -0700 Subject: [PATCH 051/127] Fix broken syntax --- README.md | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 125ca7bf..e1b93110 100644 --- a/README.md +++ b/README.md @@ -283,31 +283,31 @@ The Invocation attaches the authentication information required to authorize the ```js { sig: {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, - inv: { // ┐ - iss: "did:web:example.com", // │ - aud: "did:......FIXME", // │ - run: { // ┐ │ - act: { // ┐ │ │ - nnc: "&NCC-1701-D*", // │ │ │ - cmd: "crud/create", // │ │ │ - arg: { // │ │ │ - uri "https://example.com/blog/posts", // │ │ │ - headers: { // │ │ │ - content-type: "application/json" // │ │ │ - }, // ├── Action │ │ - payload: { // │ │ │ - title: "UCAN for Fun an Profit", // │ │ │ - body: "UCAN is great!", // │ ├── Task ├── Payload - topics: ["authz", "journal"], // │ │ │ - draft": true // │ │ │ - } // │ │ │ - } // │ │ │ - }, // ┘ │ │ - mta: { // │ │ - env: "development", // │ │ - tags: ["blog", "post", "pr#123"] // │ │ - }, // │ │ - prf: [ // │ │ + inv: { // ┐ + iss: "did:plc:ewvi7nxzyoun6zhxrhs64oiz", // │ + aud: "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", // │ + run: { // ┐ │ + act: { // ┐ │ │ + nnc: "&NCC-1701-D*", // │ │ │ + cmd: "crud/create", // │ │ │ + arg: { // │ │ │ + uri: "https://example.com/blog/posts", // │ │ │ + headers: { // │ │ │ + content-type: "application/json" // │ │ │ + }, // ├── Action │ │ + payload: { // │ │ │ + title: "UCAN for Fun an Profit", // │ │ │ + body: "UCAN is great!", // │ ├── Task ├── Payload + topics: ["authz", "journal"], // │ │ │ + draft": true // │ │ │ + } // │ │ │ + } // │ │ │ + }, // ┘ │ │ + mta: { // │ │ + env: "development", // │ │ + tags: ["blog", "post", "pr#123"] // │ │ + }, // │ │ + prf: [ // │ │ {"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}, // │ │ {"/": "bafkr4idnrqfouibxdqpvh2lmkhgsbw5yabvjbiaea3fplrb4vxifaphvgy"}, // │ │ {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"}, // │ │ From 59ec1cfbd07e8a351f13497219ce3f653e74c279 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 16:51:25 -0700 Subject: [PATCH 052/127] Consistent quotemarks --- README.md | 412 +++++++++++------------------------------------------- 1 file changed, 80 insertions(+), 332 deletions(-) diff --git a/README.md b/README.md index e1b93110..36ad3250 100644 --- a/README.md +++ b/README.md @@ -282,37 +282,37 @@ The Invocation attaches the authentication information required to authorize the ```js { - sig: {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, - inv: { // ┐ - iss: "did:plc:ewvi7nxzyoun6zhxrhs64oiz", // │ - aud: "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", // │ - run: { // ┐ │ - act: { // ┐ │ │ - nnc: "&NCC-1701-D*", // │ │ │ - cmd: "crud/create", // │ │ │ - arg: { // │ │ │ - uri: "https://example.com/blog/posts", // │ │ │ - headers: { // │ │ │ - content-type: "application/json" // │ │ │ - }, // ├── Action │ │ - payload: { // │ │ │ - title: "UCAN for Fun an Profit", // │ │ │ - body: "UCAN is great!", // │ ├── Task ├── Payload - topics: ["authz", "journal"], // │ │ │ - draft": true // │ │ │ - } // │ │ │ - } // │ │ │ - }, // ┘ │ │ - mta: { // │ │ - env: "development", // │ │ - tags: ["blog", "post", "pr#123"] // │ │ + "sig": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, + "inv": { // ┐ + "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", // │ + "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", // │ + "run": { // ┐ │ + "act": { // ┐ │ │ + "nnc": "&NCC-1701-D*", // │ │ │ + "cmd": "crud/create", // │ │ │ + "arg": { // │ │ │ + "uri": "https://example.com/blog/posts", // │ │ │ + "headers": { // │ │ │ + "content-type": "application/json" // │ │ │ + }, // ├── Action │ │ + "payload": { // │ │ │ + "title": "UCAN for Fun an Profit", // │ │ │ + "body": "UCAN is great!", // │ ├── Task ├── Payload + "topics": ["authz", "journal"], // │ │ │ + "draft": true // │ │ │ + } // │ │ │ + } // │ │ │ + }, // ┘ │ │ + "mta": { // │ │ + "env": "development", // │ │ + "tags": ["blog", "post", "pr#123"] // │ │ }, // │ │ - prf: [ // │ │ + "prf": [ // │ │ {"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}, // │ │ {"/": "bafkr4idnrqfouibxdqpvh2lmkhgsbw5yabvjbiaea3fplrb4vxifaphvgy"}, // │ │ {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"}, // │ │ ], // │ │ - exp: 1697409438 // │ │ + "exp": 1697409438 // │ │ } // ┘ │ } // ┘ } @@ -320,27 +320,36 @@ The Invocation attaches the authentication information required to authorize the Sending Email: -```json +```js { - "act": "msg/send", - "arg": { - "from" "mailto:akiko@example.com", - "to": [ - "boris@example.com", - "carol@example.com" - ], - "subject": "Coffee", - "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Invocations!" - }, - "nnc": "1234567890" + "sig": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, + "inv": { + "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", + "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", + "run": { + "act": { + "nnc": "email-akiko#1234567890" + "cmd": "msg/send", + "arg": { + "from": "mailto:akiko@example.com", + "to": [ "boris@example.com", "carol@example.com" ], + "subject": "Coffee", + "body": "Let get coffee sometime and talk about UCAN Invocations!" + } + }, + "mta": {}, + "prf": [{"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}], + "exp": 1697409438 + } + } } ``` -Running WebAssembly. In this case, the Wasm module is inlined. +Running an inlined WebAssembly module: -```json +```js { - "nnc": "", // FIXME infers idempotence + "nnc": "", // NOTE! Deterministic Wasm should always have the same nonce "act": "wasm/run", "arg": { "mod": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", @@ -350,7 +359,6 @@ Running WebAssembly. In this case, the Wasm module is inlined. } ``` - # 4 Response ## 4.1 Result @@ -377,18 +385,17 @@ A Result records the output of the [Task], as well as its success or failure sta # 4.2 Receipt -A `Receipt` is an attestation of the [Result] and requested [Effect]s by a [Task] [Invocation]. A Receipt MUST be signed by the [Executor] or it's delegate. If signed by the delegate, the proof of delegation from the [Executor] to the delegate (the `iss` of the receipt) MUST be provided in `prf`. +A `Receipt` is an attestation of the [Result] and requested [enqueued Task]s. A Receipt MUST be signed by the [Executor] (including by [Execution Proxy]). **NB: a Receipt does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. -Receipts MUST use the same version as the invocation that they contain. +Receipts MUST use the same-or-higher version number as the Invocation that they reference. -## 8.1 Receipt - -### 8.1.1 Receipt Payload +### 8.1 Receipt Payload | Field | Type | Required | Description | |-------|--------------------|----------|------------------------------------------------------------------------------------| +| `ucr` | `SemVer` | Yes | The version of this spec that the Receipt conforms to | | `iss` | `DID` | Yes | The DID of the Executor | | `ran` | `&Invocation` | Yes | MUST be a link to the [Invocation] that the Receipt is for | | `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | @@ -398,9 +405,19 @@ Receipts MUST use the same version as the invocation that they contain. | `rec` | `&Receipt` | No | Recursive `Signed`s if the Invocation was proxied to another Executor | | `iat` | `Integer`[^js-num] | No | The UTC Unix timestand at which the Receipt was issed | -A couple of these fields warrant further comment below. +A few of these fields warrant further comment below. + +### 8.1.1 Enqueue + +FIXME + +The result of an [Invocation] MAY include a request for further actions to be performed via "effects". This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. + +Enqueued [Task]s describe requests for future work to be performed. The SHOULD come with [Delegation]s + +All [Invocation]s in an [Effect] block MUST be treated as concurrent, unless explicit data dependencies between them exist via promise [Pipeline]s. The `fx` block contains two fields: `fork` and `join`. -#### 8.1.1.1 Proof +### 8.1.1 Proxy Execution Proof If the Receipt Issuer is not identical to the `aud` field of Invocation referenced in the `ran` field, a [Delegation] proof chain SHOULD be included. If a chain is present, it MUST show that @@ -437,123 +454,32 @@ const receipt = { } ``` -For u - -#### 8.1.1.2 Enqueue - -The result of an [Invocation] MAY include a request for further actions to be performed via "effects". This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. - -Enqueued [Task]s describe requests for future work to be performed. The SHOULD come with [Delegation]s - -All [Invocation]s in an [Effect] block MUST be treated as concurrent, unless explicit data dependencies between them exist via promise [Pipeline]s. The `fx` block contains two fields: `fork` and `join`. - -[Task]s listed in the `fork` field are first-class and only ordered by promises; they otherwise SHOULD be considered independent and equal. As such, atomic guarantees such as failure of one effect implying failure of other effects if left undefined. - -The `join` field describes an OPTIONAL "special" [Invocation] which instruct the [Executor] that the [Task] [Invocation] is a continuation of the previous Invocation. This roughly emulates a virtual thread which terminates in an Invocation that produces Effect without a `join` field. - -Tasks in the `fork` field MAY be related to the Task in the `join` field if there exists a Promise referencing either Task. If such a promise does not exist, then they SHOULD be treated as entirely separate and MAY be scheduled, deferred, fail, retry, and so on entirely separately. - -## 7.1 Schema - -``` -# Represents a request to invoke enclosed set of tasks concurrently -type Effects { - fx [&Invocation] -} -``` - -### 8.1.2 Receipt Envelope +## 8.2 Receipt Envelope | Field | Type | Required | Description | |-------|-------------------|----------|--------------------------------------------------------------------------| -| `uci` | `&ReceiptPayload` | Yes | The CID for the [Receipt Payload] | +| `ucr` | `&ReceiptPayload` | Yes | The CID for the [Receipt Payload] | | `sig` | `Signature` | Yes | The [Signature] of the `uci` value, by the [Receipt Payload]'s `iss` DID | -### 8.1.3 DAG-JSON Examples - -### 8.3.1 Issued by Executor +## 8.3 DAG-JSON Examples ``` js { - "ran": {"/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu"} - "out": { - "ok": { - "members": [ - "bob@example.com", - "alice@web.mail" - ] - } - }, - "mta": { - "retries": 2, - "time": [ - 400, - "hours" - ] - }, - "sig": { - "/": { - "bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU" - } - } -} -``` - -```json -{ - "ran": { "/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu" }, - "out": { - "ok": { - "members": [ "bob@example.com", "alice@web.mail" ] - } - }, - "meta": { - "retries": 2, - "time": [ 400, "hours" ] - }, - "sig": { "/": { "bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU" } } -} -``` - -### 8.3.2 Issued by Delegate - -```json -{ - "iss": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", - "prf": [ - { "/": "bafyreihfgvlol74ugosa5gkzvbsghmq7wiqn4xvgack4uwn4qagrml6p74" }, - { "/": "SOME OTHER CID FIXME" } - ], - "ran": { "/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu" }, - "out": { - "ok": { - "members": [ "bob@example.com", "alice@web.mail" ] + "ucr": { + "ran": {"/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu"} + "out": { + "ok": ["bob@example.com", "alice@example.com"] + }, + "mta": { + "retry-count": 2, + "total-time": [400, "hours"] } }, - "mta": { - "retries": 2, - "time": [ 400, "hours" ] - }, - "sig": { "/": { "bytes": "7aEDQKxIrga+88HNDd69Ho4Ggz8zkf+GxWC6dAGYua6l85YgiL3NqGxyGAygiSZtWrWUo6SokgOys2wYE7N+novtcwo" } } -} -``` - -### 8.3.3 Receipt with Enqueued Tasks - -```json -{ - "ran": { "/": "bafyreig3qnao4suz3lchh4joof7fhlobmgxhaal3vw4vtcghtlgtp7u4xy" }, - "out": { "ok": { "status": 200 } }, - "fx": [ - { "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" }, - { "/": "bafyreigmmdzix2vxboojvv6j6h7sgvxnrecdxtglwtqpxw7hybebzlsax4" }, - { "/": "bafyreif6gfpzgxnii4ys6a4bjenefg737fb5bgam3onrbmhnoa4llk244q" } - ], - "sig": { "/": { "bytes": "7aEDQAHWabtCE+QikM3Np94TrA5T8n2yXqy8Uf35hgw0fe5c2Xi1O0h/JgrFmGl2Gsbhfm05zpdQmwfK2f/Sbe00YQE" } } + "sig": {"/": {bytes: "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU"}} } ``` -# 10 Prior Art +# 9 Prior Art [ucanto RPC] from [DAG House] is a production system that uses UCAN as the basis for an RPC layer. @@ -565,7 +491,7 @@ The [Object Capability Network (OCapN)] protocol extends CapTP with a generalize [Cap 'n Proto RPC] is an influential RPC framework [based on concepts from CapTP]. -# 11 Acknowledgements +# 10 Acknowledgements Many thanks to [Mark Miller] for his [pioneering work] on [capability systems]. @@ -610,181 +536,3 @@ Thanks to [Christine Lemmer-Webber] for the many conversations about capability [eRights]: https:/erights.org [promise pipelines]: http://erights.org/elib/distrib/pipeline.html [ucanto RPC]: https://github.com/web3-storage/ucanto - - - - - - - - - - - - - - - - - - - - - - - - -```json -{ - iss: "did:example:bob", - aud: "did:example:alice", - act: "msg/send", - nnc: "", - arg: { - from: "mailto:alice@example.com", - to: [ - "bob@example.com", - "carol@example.com" - ], - subject: "hello", - body: "world" - } - meta: { - fuel: 100, - disk: "100GB" - }, - prf: [bafy1, bafy2] -} - - - - - - - -``` js -{ - iss: "did:example:bob", - aud: "did:example:alice", // Origoinally removed these because it's duplicated from prf, but important for e.g. CRDT - run: { - sub: "did:example:alice", // <- where, IFF the subject is relevant... only really useful for - cmd: "counter/inc", - arg: {by: 4} - }, - meta: {}, - cause: {"/": "bafy...123"}, - prf: [bafy1, bafy3], - exp: 999999 // Doubles as a handy timeout -} -``` - -``` js -{ - iss: "did:example:bob", - aud: "did:example:alice", // NOTE: can be ANYONE in the delegation chain for proxying if you don't have a direct path? - exp: 999999, - run: { - act: "wasm/run", - arg: { - mod: "ipfs://...", - fun: "add_one", - arg: [42] - } - }, - prf: [bafy1, bafy3] -} -``` - - -NOTE TO SELF: keep aud field as optional. i.e. make it salient for ergonomic reasons / push it into the task writer's face. also aud & sub MAY diverge in the future. - - - - - -NOTE: can make great use of batrch signatures -NOTE TO SELF: batch signatures need trees, too? - -``` mermaid -sequenceDiagram - actor User - - participant Service - - participant WorkQueue - participant Worker1 - participant Worker2 - - autonumber - - Note over Service, Worker2: Service Setup - WorkQueue -->> Service: delegate(WorkQueue, queue/push) - Service -->> WorkQueue: delegate(Service, ucan/proxy/sign) - - WorkQueue -->> Worker1: delegate(WorkQueue, queue/pop) - WorkQueue -->> Worker1: delegate(Service, ucan/proxy/sign) - - WorkQueue -->> Worker2: delegate(WorkQueue, queue/pop) - WorkQueue -->> Worker2: delegate(Service, ucan/proxy/sign) - - Note over User, Service: Delegates to User - Service -->> User: delegate(Service, crud/update) - - Note over User, Worker2: Invocation with Proxy Execution - User ->> Service: invoke(Service, [crud/update, "foo", 42], prf: [➐]) - Service -) WorkQueue: invoke(WorkQueue, queue/push, [➑], prf: [➊]) - - Note over WorkQueue, Worker2: Work Stealing - Worker2 ->>+ WorkQueue: invoke(WorkQueue, queue/pop, prf: [➎]) - WorkQueue ->>- Worker2: receipt(inv: ➓, out: ➒) - Worker2 ->> Worker2: Execute!(➒) - Worker2 -) User: receipt(out: ok, inv: ➒, prf: [➏,➋]) -``` - - - - - - - - - - - - - - - - -> Bob: Delegate - Bob -->> Carol 📧: Delegate - Carol 📧 -->> Dan: Delegate - Carol 📧 -->> Dan: Delegate - - Note over Alice 💾, Dan: Single Invocation - Dan ->> Alice 💾: Read from Alice's DB! - Alice 💾 -->> Dan: Result<➎> - - Note over Alice 💾, Dan: Multiple Invocation Flow - Dan ->> Alice 💾: Read from Alice's DB! - Alice 💾 -->> Dan: Result<➐> - Dan ->> Carol 📧: Send email containing Result<➐> as Carol! - Carol 📧 ->> Carol 📧: Send email! - - Note over Alice 💾, Dan: Promise Pipeline - Dan ->> Alice 💾: Read from Alice's DB! - Dan ->> Carol 📧: Send email containing Result<⓫> as Carol! - Alice 💾 -->> Carol 📧: Result<⓫> - Carol 📧 ->> Carol 📧: Send email containing Result<⓫> as Carol! ---> From a66c0c6ba4bec422330aeaf1726ad3301ec010ec Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 16:56:59 -0700 Subject: [PATCH 053/127] Fix internal links --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 36ad3250..31da6136 100644 --- a/README.md +++ b/README.md @@ -509,12 +509,23 @@ Thanks to [Christine Lemmer-Webber] for the many conversations about capability +[Arguments]: #312-arguments +[Command]: #311-command +[Invocation]: #33-invocation +[Nonce]: #314-nonce +[Receipt]: #42-receipt +[Response]: #4-response +[Result]: #41-result +[Subject]: #313-subject +[Task]: #32-task + [BCP 14]: https://www.rfc-editor.org/info/bcp14 [Bacalhau]: https://www.bacalhau.org/ [Brooklyn Zelenka]: https://github.com/expede/ [DAG House]: https://dag.house +[Delegation]: https://github.com/ucan-wg/delegation [E-lang Mailing List, 2000 Oct 18]: http://wiki.erights.org/wiki/Capability-based_Active_Invocation_Certificates [Fission]: https://fission.codes/ [Haskell]: https://en.wikipedia.org/wiki/Haskell From 5977ff6a2ab5359048c502e98c17adcfe34e4fa5 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 20:21:51 -0700 Subject: [PATCH 054/127] Fix typo --- README.md | 86 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 31da6136..6b215d66 100644 --- a/README.md +++ b/README.md @@ -187,12 +187,12 @@ flowchart RL A Command is the smallest unit of work that can be Invoked. It is akin to a function call or actor message. It MUST conform to the following struct shape: -| Name | Field | Type | Required | -|-------------|-------|---------------------|----------| -| [Subject] | `sub` | DID | No | -| [Command] | `cmd` | String | Yes | -| [Arguments] | `arg` | Map `{String: Any}` | Yes | -| [Nonce] | `nnc` | String | Yes | +| Name | Field | Type | Required | +|-------------|-------|------------------|----------| +| [Subject] | `sub` | `DID` | No | +| [Command] | `cmd` | `String` | Yes | +| [Arguments] | `arg` | `{String : Any}` | Yes | +| [Nonce] | `nnc` | `String` | Yes | The `arg` field MUST be defined by the `cmd` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. @@ -200,13 +200,10 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to ```json { - "act": "msg/send", + "cmd": "msg/send", "arg": { "from": "mailto:alice@example.com", - "to": [ - "bob@example.com", - "carol@example.com" - ], + "to": [ "bob@example.com", "carol@example.com" ], "subject": "hello", "body": "world" } @@ -215,13 +212,12 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to ```js // Pseudocode -() => - msg.send({ - from: "mailto:alice@example.com", - to: ["bob@example.com", "carol@example.com"], - subject: "hello", - body: "world" - }) +() => msg.send({ + from: "mailto:alice@example.com", + to: ["bob@example.com", "carol@example.com"], + subject: "hello", + body: "world" +}) ``` ### 3.1.1 Command @@ -266,15 +262,18 @@ Tasks wrap a [Command] with contextual information. This includes expiration tim ## 3.3 Invocation -The Invocation attaches the authentication information required to authorize the [Task]. It consists of an InvocationPayload, and a signture envelope. +The Invocation attaches the authentication information required to authorize the [Task]. It consists of an [Invocation Payload], and a signture envelope. -### 3.3.1 InvocationPayload +### 3.3.1 Invocation Payload + +FIXME ### 3.3.2 Invocation (Envelope) -| Field | Type | Required | Description | -|------|-------|--------|-------------| -| `uci` | `InvocationPayload` | Yes | +| Field | Type | Required | Description | +|-------|----------------------|----------|-------------| +| `uci` | `&InvocationPayload` | Yes | | +| `sig` | `&Signature` | Yes | FIXME | ## 3.4 Examples @@ -286,8 +285,8 @@ The Invocation attaches the authentication information required to authorize the "inv": { // ┐ "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", // │ "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", // │ - "run": { // ┐ │ - "act": { // ┐ │ │ + "run": cid({ // ┐ │ + "act": cid({ // ┐ │ │ "nnc": "&NCC-1701-D*", // │ │ │ "cmd": "crud/create", // │ │ │ "arg": { // │ │ │ @@ -302,7 +301,7 @@ The Invocation attaches the authentication information required to authorize the "draft": true // │ │ │ } // │ │ │ } // │ │ │ - }, // ┘ │ │ + }), // ┘ │ │ "mta": { // │ │ "env": "development", // │ │ "tags": ["blog", "post", "pr#123"] // │ │ @@ -313,12 +312,12 @@ The Invocation attaches the authentication information required to authorize the {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"}, // │ │ ], // │ │ "exp": 1697409438 // │ │ - } // ┘ │ + }) // ┘ │ } // ┘ } ``` -Sending Email: +### 3.4.2 Sending Email ```js { @@ -345,7 +344,7 @@ Sending Email: } ``` -Running an inlined WebAssembly module: +### 3.4.3 Remote Execution of Inline WebAssembly ```js { @@ -393,17 +392,17 @@ Receipts MUST use the same-or-higher version number as the Invocation that they ### 8.1 Receipt Payload -| Field | Type | Required | Description | -|-------|--------------------|----------|------------------------------------------------------------------------------------| -| `ucr` | `SemVer` | Yes | The version of this spec that the Receipt conforms to | -| `iss` | `DID` | Yes | The DID of the Executor | -| `ran` | `&Invocation` | Yes | MUST be a link to the [Invocation] that the Receipt is for | -| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | -| `out` | `Result` | Yes | MUST contain the value output of the invocation in [Result] format | -| `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | -| `mta` | `{String : Any}` | Yes | Additional data about the receipt | -| `rec` | `&Receipt` | No | Recursive `Signed`s if the Invocation was proxied to another Executor | -| `iat` | `Integer`[^js-num] | No | The UTC Unix timestand at which the Receipt was issed | +| Field | Type | Required | Description | +|-------|---------------------------|----------|------------------------------------------------------------------------------------| +| `ucr` | `SemVer` | Yes | The version of this spec that the Receipt conforms to | +| `iss` | `DID` | Yes | The DID of the Executor | +| `ran` | `&Invocation` | Yes | MUST be a link to the [Invocation] that the Receipt is for | +| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | +| `out` | `Result` | Yes | MUST contain the value output of the invocation in [Result] format | +| `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | +| `mta` | `{String : Any}` | Yes | Additional data about the receipt | +| `rec` | `&Receipt` | No | Recursive `Signed`s if the Invocation was proxied to another Executor | +| `iat` | `Integer | null`[^js-num] | No | The UTC Unix timestand at which the Receipt was issed | A few of these fields warrant further comment below. @@ -507,6 +506,10 @@ Thanks to [Philipp Krüger] for the enthusiastic feedback on the overall design Thanks to [Christine Lemmer-Webber] for the many conversations about capability systems and the programming models that they enable. + + +[^js-num]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined as a [IEEE-754] double-precision floating point number, which has a 53-bit significand. + [Arguments]: #312-arguments @@ -531,6 +534,7 @@ Thanks to [Christine Lemmer-Webber] for the many conversations about capability [Haskell]: https://en.wikipedia.org/wiki/Haskell [IPVM]: https://github.com/ipvm-wg [Irakli Gozalishvili]: https://github.com/Gozala +[JS Number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number [Luke Marsen]: https://github.com/lukemarsden [Mark Miller]: https://github.com/erights [Philipp Krüger]: https://github.com/matheus23/ @@ -545,5 +549,5 @@ Thanks to [Christine Lemmer-Webber] for the many conversations about capability [`ipfs`]: https://docs.ipfs.tech/how-to/address-ipfs-on-web/#native-urls [`magnet`]: https://en.wikipedia.org/wiki/Magnet_URI_scheme [eRights]: https:/erights.org -[promise pipelines]: http://erights.org/elib/distrib/pipeline.html +[distributed promise pipelines]: http://erights.org/elib/distrib/pipeline.html [ucanto RPC]: https://github.com/web3-storage/ucanto From 657f622aed30912d4d2f1ecd75ee3b064884d366 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 20:43:00 -0700 Subject: [PATCH 055/127] Fix some old terminology --- README.md | 74 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 6b215d66..f31599f0 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,5 @@ # UCAN Invocation Specification v1.0.0-rc. 1 -FIXME show proxy invocation -FIXME show delegated execution (work stealing) example -FIXME move pipelines, await, etc to own spec FIXME explain in FAQ why we don't need `join` (i.e. use promises, since the model is inverted) FIXME reverse lookup secion @@ -108,14 +105,12 @@ The invoker MUST be the UCAN delegator. Their DID MUST be authenticated in the ` The executor is directed to perform some task described in the UCAN by the invoker. -The executor MUST be the UCAN delegate. Their DID MUST be set the in `aud` field of the contained UCAN. - ## 2.2 Lifecycle At a very high level: - A [Task] absractly requests some action -- An [Invocation] attaches proven ([Delegation]) authority to a [Task] +- An [Invocation] attaches proven ([delegated][Delegation]) authority to a [Task] - A [Receipt] MAY request that the Invoker enqueue more [Task]s ``` mermaid @@ -183,7 +178,7 @@ flowchart RL # 3 Request -## 3.1 Command +## 3.1 Action A Command is the smallest unit of work that can be Invoked. It is akin to a function call or actor message. It MUST conform to the following struct shape: @@ -196,9 +191,10 @@ A Command is the smallest unit of work that can be Invoked. It is akin to a func The `arg` field MUST be defined by the `cmd` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. -Using the JavaScript analogy from the introduction, an Instruction is similar to wrapping a call in a closure: +Using the JavaScript analogy from the introduction, an Action is similar to wrapping a call in a closure: -```json +```js +// Command { "cmd": "msg/send", "arg": { @@ -211,7 +207,7 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to ``` ```js -// Pseudocode +// Pseudocode JS Analogy () => msg.send({ from: "mailto:alice@example.com", to: ["bob@example.com", "carol@example.com"], @@ -222,7 +218,7 @@ Using the JavaScript analogy from the introduction, an Instruction is similar to ### 3.1.1 Command -The Command (`cmd`) field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a [UCAN Ability], which includes heirarchy, an Operation MUST be fully concrete. +The Command (`cmd`) field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a UCAN Delegation [Ability], which includes heirarchy, an Operation MUST be fully concrete. ### 3.1.2 Arguments @@ -230,8 +226,6 @@ The Arguments (`arg`) field, MAY contain any parameters expected by the Resource UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type of `input`s allowed. -If the `input` field is not present, it is implicitly a `unit` represented as empty map. - ### 3.1.3 Subject The OPTIONAL `sub` field is intended for cases where parametrizing a specific agent is important. This is especially critical for two parts of the lifecycle: @@ -241,17 +235,11 @@ The OPTIONAL `sub` field is intended for cases where parametrizing a specific ag ### 3.1.4 Nonce -If present, the REQUIRED `nnc` field MUST include a random nonce expressed in ASCII. This field ensures that multiple (non-idempotent) invocations are unique. The nonce SHOULD be `""` for Commands that are idempotent. +If present, the REQUIRED `nnc` field MUST include a random nonce expressed in ASCII. This field ensures that multiple (non-idempotent) invocations are unique. The nonce SHOULD be `""` for Commands that are idempotent (such as determinstic Wasm modules or standards-abiding HTTP PUT requests). ## 3.2 Task -As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is an instruction to the [Executor] to perform enclosed [Task]. [Invocation]s are not executable until they have been provided provable authority (in form of UCANs in the `prf` field) and an [Authorization] (in the `auth` field) from the [Invoker]. - -The `auth` field MUST be contain an [Authorization] which signs over the `&Task` in `run`. - -Concretely, this means that the `&Task` MUST be present in the associated `auth`'s `scope` field. A `Receipt` where the associated [Authorization] does not include the [Task] in the `scope` MUST be considered invalid. - -Tasks wrap a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. +A Task wraps a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. | Field | Type | Description | Required | |-------|---------------------------|----------------------------------------------------------------------------------------------|----------| @@ -260,20 +248,29 @@ Tasks wrap a [Command] with contextual information. This includes expiration tim | `prf` | `[&Delegation]` | Links to any [UCAN Delegation]s that provide the authority to perform the enclosed [Command] | Yes | | `exp` | `Integer | null`[^js-num] | The UTC Unix timestamp at which the Task expires | Yes | +The CID of a Task is useful for reverse lookups in [Receipt]-sharing networks to check if someone else has run this Task before, and in [UCAN Promise] to connect Tasks together. + ## 3.3 Invocation -The Invocation attaches the authentication information required to authorize the [Task]. It consists of an [Invocation Payload], and a signture envelope. +As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is an instruction to the [Executor] to perform enclosed [Task]. [Invocation Payloads]s are not executable until they have been 1. signed, and 2. checked that the [Delegation] proofs are correct. ### 3.3.1 Invocation Payload -FIXME +The Invocation Payload attaches sender, receiver, and provenanial information to the [Task] + +| Field | Type | Required | Description | +|-------|------------|----------|-----------------------------------------------------------| +| `iss` | `DID` | Yes | The DID of the [Invoker] | +| `aud` | `DID` | Yes | The DID of the intended [Executor] | +| `run` | `&Task` | Yes | The enclosed [Task]'s CID | +| `cau` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | ### 3.3.2 Invocation (Envelope) -| Field | Type | Required | Description | -|-------|----------------------|----------|-------------| -| `uci` | `&InvocationPayload` | Yes | | -| `sig` | `&Signature` | Yes | FIXME | +| Field | Type | Required | Description | +|-------|----------------------|----------|----------------------------------------------------------| +| `uci` | `&InvocationPayload` | Yes | The CID of the [Invocation Payload] | +| `sig` | `&Signature` | Yes | A signature by the Payload's `iss` over the CID in `uci` | ## 3.4 Examples @@ -344,16 +341,25 @@ FIXME } ``` -### 3.4.3 Remote Execution of Inline WebAssembly +### 3.4.3 Inline WebAssembly ```js { - "nnc": "", // NOTE! Deterministic Wasm should always have the same nonce - "act": "wasm/run", - "arg": { - "mod": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", - "fun": "add_one", - "params": [42] + "sig": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, + "inv": { + "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", + "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", + "run": { + "act": { + "nnc": "", // NOTE: as stated above, idempotent Actions should always have the same nonce + "act": "wasm/run", + "arg": { + "mod": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", + "fun": "add_one", + "params": [42] + } + } + } } } ``` From 41a0cdeff0fba32b593aebed80d5432636867086 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 21:04:39 -0700 Subject: [PATCH 056/127] internal links --- README.md | 79 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index f31599f0..68b4d7fc 100644 --- a/README.md +++ b/README.md @@ -18,14 +18,14 @@ FIXME reverse lookup secion - [UCAN Delegation] -# 0 Abstract - -UCAN Invocation defines a format for expressing the intention to execute delegated UCAN capabilities, and the attested receipts from an execution. - ## Language The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [BCP 14] when, and only when, they appear in all capitals, as shown here. +# 0 Abstract + +UCAN Invocation defines a format for expressing the intention to execute delegated UCAN capabilities, and the attested receipts from an execution. + # 1 Introduction > Just because you can doesn't mean that you should @@ -224,7 +224,7 @@ The Command (`cmd`) field MUST contain a concrete operation that can be sent to The Arguments (`arg`) field, MAY contain any parameters expected by the Resource/Operation pair, which MAY be different between different Resources and Operations, and is thus left to the executor to define the shape of this data. This field MUST be representable as a map or keyword list. -UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type of `input`s allowed. +UCAN capabilities provided in proofs MAY impose certain constraint on the type of Arguments allowed. ### 3.1.3 Subject @@ -241,22 +241,22 @@ If present, the REQUIRED `nnc` field MUST include a random nonce expressed in AS A Task wraps a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. -| Field | Type | Description | Required | -|-------|---------------------------|----------------------------------------------------------------------------------------------|----------| -| `run` | `&Command` | The `run` field MUST contain a link to the [Task] to be run | Yes | -| `mta` | `{String : Any}` | Extensible fields, e.g. resource limits, human-readable tags, notes, and so on | Yes | -| `prf` | `[&Delegation]` | Links to any [UCAN Delegation]s that provide the authority to perform the enclosed [Command] | Yes | -| `exp` | `Integer | null`[^js-num] | The UTC Unix timestamp at which the Task expires | Yes | +| Field | Type | Description | Required | +|-------|----------------------------|----------------------------------------------------------------------------------------------|----------| +| `run` | `&Command` | The `run` field MUST contain a link to the [Task] to be run | Yes | +| `mta` | `{String : Any}` | Extensible fields, e.g. resource limits, human-readable tags, notes, and so on | Yes | +| `prf` | `[&Delegation]` | Links to any [UCAN Delegation]s that provide the authority to perform the enclosed [Command] | Yes | +| `exp` | `Integer \| null`[^js-num] | The UTC Unix timestamp at which the Task expires | Yes | The CID of a Task is useful for reverse lookups in [Receipt]-sharing networks to check if someone else has run this Task before, and in [UCAN Promise] to connect Tasks together. ## 3.3 Invocation -As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is an instruction to the [Executor] to perform enclosed [Task]. [Invocation Payloads]s are not executable until they have been 1. signed, and 2. checked that the [Delegation] proofs are correct. +As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is an instruction to the [Executor] to perform enclosed [Task]. [Invocation Payload]s are not executable until they have been signed and [Delegation] proofs validated. ### 3.3.1 Invocation Payload -The Invocation Payload attaches sender, receiver, and provenanial information to the [Task] +The Invocation Payload attaches sender, receiver, and provenanial information to the [Task]. | Field | Type | Required | Description | |-------|------------|----------|-----------------------------------------------------------| @@ -390,13 +390,13 @@ A Result records the output of the [Task], as well as its success or failure sta # 4.2 Receipt -A `Receipt` is an attestation of the [Result] and requested [enqueued Task]s. A Receipt MUST be signed by the [Executor] (including by [Execution Proxy]). +A `Receipt` is an attestation of the [Result] and requested [enqueued Tasks][enqueue]. A Receipt MUST be signed by the [Executor] (including by [Execution Proxy]). **NB: a Receipt does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. Receipts MUST use the same-or-higher version number as the Invocation that they reference. -### 8.1 Receipt Payload +### 4.1.1 Receipt Payload | Field | Type | Required | Description | |-------|---------------------------|----------|------------------------------------------------------------------------------------| @@ -412,7 +412,7 @@ Receipts MUST use the same-or-higher version number as the Invocation that they A few of these fields warrant further comment below. -### 8.1.1 Enqueue +### 4.1.1.1 Enqueue FIXME @@ -420,9 +420,9 @@ The result of an [Invocation] MAY include a request for further actions to be pe Enqueued [Task]s describe requests for future work to be performed. The SHOULD come with [Delegation]s -All [Invocation]s in an [Effect] block MUST be treated as concurrent, unless explicit data dependencies between them exist via promise [Pipeline]s. The `fx` block contains two fields: `fork` and `join`. +All [Tasks]s in an [enqueue] array MUST be treated as concurrent, unless explicit data dependencies between them exist via promise [Pipeline]s. -### 8.1.1 Proxy Execution Proof +### 4.1.1.2 Proxy Execution If the Receipt Issuer is not identical to the `aud` field of Invocation referenced in the `ran` field, a [Delegation] proof chain SHOULD be included. If a chain is present, it MUST show that @@ -459,14 +459,14 @@ const receipt = { } ``` -## 8.2 Receipt Envelope +## 4.2 Receipt Envelope | Field | Type | Required | Description | |-------|-------------------|----------|--------------------------------------------------------------------------| | `ucr` | `&ReceiptPayload` | Yes | The CID for the [Receipt Payload] | | `sig` | `Signature` | Yes | The [Signature] of the `uci` value, by the [Receipt Payload]'s `iss` DID | -## 8.3 DAG-JSON Examples +## 4.3 DAG-JSON Examples ``` js { @@ -484,21 +484,21 @@ const receipt = { } ``` -# 9 Prior Art +# 5 Prior Art [ucanto RPC] from [DAG House] is a production system that uses UCAN as the basis for an RPC layer. The [Capability Transport Protocol (CapTP)] is one of the most influential object-capability systems, and forms the basis for much of the rest of the items on this list. -The [Object Capability Network (OCapN)] protocol extends CapTP with a generalized networking layer. It has implementations from the [Spritely Institute] and [Agoric]. At time of writing, it is in the process of being standardized. +The Object Capability Network ([OCapN]) protocol extends [CapTP] with a generalized networking layer. It has implementations from the [Spritely Institute] and [Agoric]. At time of writing, it is in the process of being standardized. -[Electronic Rights Transfer Protocol (ERTP)] builds on top of CapTP for blockchain & digital asset use cases. +[Electronic Rights Transfer Protocol (ERTP)] builds on top of [CapTP] concepts for blockchain & digital asset use cases. -[Cap 'n Proto RPC] is an influential RPC framework [based on concepts from CapTP]. +[Cap 'n Proto RPC] is an influential RPC framework based on concepts from [CapTP]. -# 10 Acknowledgements +# 6 Acknowledgements -Many thanks to [Mark Miller] for his [pioneering work] on [capability systems]. +Many thanks to [Mark Miller] for his [trail blazing work][erights] on [capability systems]. Many thanks to [Luke Marsen] and [Simon Worthington] for their feedback on invocation model from their work on [Bacalhau] and [IPVM]. @@ -512,40 +512,61 @@ Thanks to [Philipp Krüger] for the enthusiastic feedback on the overall design Thanks to [Christine Lemmer-Webber] for the many conversations about capability systems and the programming models that they enable. +Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the general IPLD worldview + [^js-num]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined as a [IEEE-754] double-precision floating point number, which has a 53-bit significand. - + [Arguments]: #312-arguments [Command]: #311-command +[Execution Proxy]: #41112-proxy-execution +[Executor]: #212-executor +[Invocation Payload]: #331-invocation-payload +[Invocation Signature]: #331-invocation-envelope [Invocation]: #33-invocation +[Invoker]: #211-invoker [Nonce]: #314-nonce +[Receipt Payload]: #411-receipt-payload [Receipt]: #42-receipt [Response]: #4-response [Result]: #41-result [Subject]: #313-subject [Task]: #32-task +[enqueue]: #4111-enqueue +[lazy-vs-eager]: #112-lazy-vs-eager-evaluation +[Ability]: https://github.com/ucan-wg/delegation#33-abilities +[Agoric]: https://agoric.com/ [BCP 14]: https://www.rfc-editor.org/info/bcp14 [Bacalhau]: https://www.bacalhau.org/ +[Blaine Cook]: https://github.com/blaine [Brooklyn Zelenka]: https://github.com/expede/ +[CapTP]: https://capnproto.org/rpc.html#specification +[Christine Lemmer-Webber]: https://github.com/cwebber [DAG House]: https://dag.house [Delegation]: https://github.com/ucan-wg/delegation [E-lang Mailing List, 2000 Oct 18]: http://wiki.erights.org/wiki/Capability-based_Active_Invocation_Certificates +[Electronic Rights Transfer Protocol (ERTP)]: https://docs.agoric.com/guides/ertp/ [Fission]: https://fission.codes/ [Haskell]: https://en.wikipedia.org/wiki/Haskell [IPVM]: https://github.com/ipvm-wg [Irakli Gozalishvili]: https://github.com/Gozala [JS Number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number [Luke Marsen]: https://github.com/lukemarsden +[Marc-Antoine Parent]: https://github.com/maparent [Mark Miller]: https://github.com/erights +[OCapN]: https://github.com/ocapn/ [Philipp Krüger]: https://github.com/matheus23/ +[Quinn Wilton]: https://github.com/QuinnWilton [Robust Composition]: http://www.erights.org/talks/thesis/markm-thesis.pdf +[Rod Vagg]: https://github.com/rvagg/ [Simon Worthington]: https://github.com/simonwo +[Spritely Institute]: https://spritely.institute/news/introducing-a-distributed-debugger-for-goblins-with-time-travel.html [UCAN Ability]: https://github.com/ucan-wg/delegation/#23-ability [UCAN Delegation]: https://github.com/ucan-wg/delegation/ [UCAN Promise]: https://github.com/ucan-wg/promise/ @@ -554,6 +575,8 @@ Thanks to [Christine Lemmer-Webber] for the many conversations about capability [`data`]: https://en.wikipedia.org/wiki/Data_URI_scheme [`ipfs`]: https://docs.ipfs.tech/how-to/address-ipfs-on-web/#native-urls [`magnet`]: https://en.wikipedia.org/wiki/Magnet_URI_scheme -[eRights]: https:/erights.org +[capability systems]: https://en.wikipedia.org/wiki/Capability-based_security [distributed promise pipelines]: http://erights.org/elib/distrib/pipeline.html +[eRights]: https:/erights.org +[erights]: https://erights.org [ucanto RPC]: https://github.com/web3-storage/ucanto From 0a686838ec9e34f0f0ed53407c7b2f662719b114 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 21:23:47 -0700 Subject: [PATCH 057/127] Remove fixmes --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 68b4d7fc..b6f10e52 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,5 @@ # UCAN Invocation Specification v1.0.0-rc. 1 -FIXME explain in FAQ why we don't need `join` (i.e. use promises, since the model is inverted) -FIXME reverse lookup secion - ## Editors - [Brooklyn Zelenka], [Fission] @@ -16,6 +13,8 @@ FIXME reverse lookup secion ## Depends On +- [DAG-CBOR] +- [DID] - [UCAN Delegation] ## Language @@ -549,6 +548,8 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [CapTP]: https://capnproto.org/rpc.html#specification [Christine Lemmer-Webber]: https://github.com/cwebber [DAG House]: https://dag.house +[DAG-CBOR]: https://ipld.io/specs/codecs/dag-cbor/spec/ +[DID]: https://www.w3.org/TR/did-core/ [Delegation]: https://github.com/ucan-wg/delegation [E-lang Mailing List, 2000 Oct 18]: http://wiki.erights.org/wiki/Capability-based_Active_Invocation_Certificates [Electronic Rights Transfer Protocol (ERTP)]: https://docs.agoric.com/guides/ertp/ From 06e4e1dc07c1c96c6598f23dab2deb70eedaee64 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 21:26:07 -0700 Subject: [PATCH 058/127] Fix some dangling links --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b6f10e52..d33b0a47 100644 --- a/README.md +++ b/README.md @@ -413,17 +413,15 @@ A few of these fields warrant further comment below. ### 4.1.1.1 Enqueue -FIXME +The result of an [Invocation] MAY include a request for further actions to be performed. This is a process of requesting that the invoker "enqueue" a new Task. This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. -The result of an [Invocation] MAY include a request for further actions to be performed via "effects". This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. - -Enqueued [Task]s describe requests for future work to be performed. The SHOULD come with [Delegation]s +Enqueued [Task]s describe requests for future work to be performed. They SHOULD come with [Delegation]s, but MAY be a simple request back to the Invoker. All [Tasks]s in an [enqueue] array MUST be treated as concurrent, unless explicit data dependencies between them exist via promise [Pipeline]s. ### 4.1.1.2 Proxy Execution -If the Receipt Issuer is not identical to the `aud` field of Invocation referenced in the `ran` field, a [Delegation] proof chain SHOULD be included. If a chain is present, it MUST show that +If the Receipt Issuer is not identical to the `aud` field of Invocation referenced in the `ran` field, a [Delegation] proof chain SHOULD be included. If a chain is present, it MUST show that such a proxy execution was authorized by the original listed `aud` Agent. ``` js // Pseudocode From 9ab3b0a44715ec0de5dc625c10d49dc25df77ec0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 21:28:52 -0700 Subject: [PATCH 059/127] typos --- README.md | 2 +- invocation.ipldsch | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d33b0a47..dd704885 100644 --- a/README.md +++ b/README.md @@ -305,7 +305,7 @@ The Invocation Payload attaches sender, receiver, and provenanial information to "prf": [ // │ │ {"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}, // │ │ {"/": "bafkr4idnrqfouibxdqpvh2lmkhgsbw5yabvjbiaea3fplrb4vxifaphvgy"}, // │ │ - {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"}, // │ │ + {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"} // │ │ ], // │ │ "exp": 1697409438 // │ │ }) // ┘ │ diff --git a/invocation.ipldsch b/invocation.ipldsch index f680895d..44d7d91d 100644 --- a/invocation.ipldsch +++ b/invocation.ipldsch @@ -1,6 +1,6 @@ type Invocation struct { inv &InvocationPayload - sig &Signature + sig Signature } type InvocationPayload struct { From da89888c3cf78c401b02e59c6b8d79696cf8ab04 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 21:40:00 -0700 Subject: [PATCH 060/127] Fix much spelling --- .github/workflows/words-to-ignore.txt | 15 +++++++++++++-- README.md | 14 +++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index ddcd7a62..99f2233b 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -2,13 +2,16 @@ Acknowledgements Agoric Akiko Akiko's +BCP Bacalhau +CBOR CIDs Cap'N Cap'n CapTP DAG-CBOR DAG-JSON +E-Lang ERTP Enum Gozalishvili @@ -45,36 +48,46 @@ URI URIs Vagg Varsig +Wasm WebAssembly Worthington Zeeshan Zelenka +args atomicity auth backend base64url canonicalized +cryptographic cryptographically dataflow de delegator dholms +enqueuing expede facto implicits interpretable invariants invoker +js-num +memoization multiformat outmost parallelize pipelined pipelining pre-vacation +proxied +rc referentially +representable requestor's responder signalling +significand simple-but-evolvable struct subdelegated @@ -87,5 +100,3 @@ unpadded url v0 validator -cryptographic -CBOR diff --git a/README.md b/README.md index dd704885..0f6cd951 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ By distinguishing invocation from delegation, agents are able to understand the ## 1.3 Public Resources -A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard required for initiating flow (e.g. signup). These cases typically involve a reference passed out of band (such as a web link). Due to [designation without authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain , but this SHOULD NOT be the default behaviour. +A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard required for initiating flow (e.g. sign up). These cases typically involve a reference passed out of band (such as a web link). Due to [designation without authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain , but this SHOULD NOT be the default behavior. ## 1.4 Promise Pipelining @@ -104,11 +104,11 @@ The invoker MUST be the UCAN delegator. Their DID MUST be authenticated in the ` The executor is directed to perform some task described in the UCAN by the invoker. -## 2.2 Lifecycle +## 2.2 Life Cycle At a very high level: -- A [Task] absractly requests some action +- A [Task] abstractly requests some action - An [Invocation] attaches proven ([delegated][Delegation]) authority to a [Task] - A [Receipt] MAY request that the Invoker enqueue more [Task]s @@ -217,7 +217,7 @@ Using the JavaScript analogy from the introduction, an Action is similar to wrap ### 3.1.1 Command -The Command (`cmd`) field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a UCAN Delegation [Ability], which includes heirarchy, an Operation MUST be fully concrete. +The Command (`cmd`) field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a UCAN Delegation [Ability], which includes hierarchy, an Operation MUST be fully concrete. ### 3.1.2 Arguments @@ -227,14 +227,14 @@ UCAN capabilities provided in proofs MAY impose certain constraint on the type o ### 3.1.3 Subject -The OPTIONAL `sub` field is intended for cases where parametrizing a specific agent is important. This is especially critical for two parts of the lifecycle: +The OPTIONAL `sub` field is intended for cases where parameterizing a specific agent is important. This is especially critical for two parts of the life cycle: 1. Specifying a particular `sub` (and thus `aud`) when [enqueuing new Tasks][enqueue] in a Receipt 2. Indexing Receipts for reverse lookup and memoization ### 3.1.4 Nonce -If present, the REQUIRED `nnc` field MUST include a random nonce expressed in ASCII. This field ensures that multiple (non-idempotent) invocations are unique. The nonce SHOULD be `""` for Commands that are idempotent (such as determinstic Wasm modules or standards-abiding HTTP PUT requests). +If present, the REQUIRED `nnc` field MUST include a random nonce expressed in ASCII. This field ensures that multiple (non-idempotent) invocations are unique. The nonce SHOULD be `""` for Commands that are idempotent (such as deterministic Wasm modules or standards-abiding HTTP PUT requests). ## 3.2 Task @@ -255,7 +255,7 @@ As [noted in the introduction][lazy-vs-eager], there is a difference between a r ### 3.3.1 Invocation Payload -The Invocation Payload attaches sender, receiver, and provenanial information to the [Task]. +The Invocation Payload attaches sender, receiver, and provenance to the [Task]. | Field | Type | Required | Description | |-------|------------|----------|-----------------------------------------------------------| From 95fbd348f5b235f158325f3d24cd32c08932cfd5 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 21:46:35 -0700 Subject: [PATCH 061/127] Such spelling --- .github/workflows/words-to-ignore.txt | 5 +++-- README.md | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 99f2233b..9ccdbf7e 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -11,7 +11,7 @@ Cap'n CapTP DAG-CBOR DAG-JSON -E-Lang +E-lang ERTP Enum Gozalishvili @@ -53,7 +53,7 @@ WebAssembly Worthington Zeeshan Zelenka -args +arg atomicity auth backend @@ -77,6 +77,7 @@ memoization multiformat outmost parallelize +parameterizing pipelined pipelining pre-vacation diff --git a/README.md b/README.md index 0f6cd951..e36ca2ae 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ UCAN Invocation defines a format for expressing the intention to execute delegat > > — Anonymous -> When authorizaton is communicated without such context, it's like receiving a key in the mail with no hint about what to do with it [...] After an object receives this message, she can invoke arg if she chooses, but why would she ever choose to do so? +> When authorization is communicated without such context, it's like receiving a key in the mail with no hint about what to do with it [...] After an object receives this message, she can invoke arg if she chooses, but why would she ever choose to do so? > > Mark Miller, [E-lang Mailing List, 2000 Oct 18] @@ -247,7 +247,7 @@ A Task wraps a [Command] with contextual information. This includes expiration t | `prf` | `[&Delegation]` | Links to any [UCAN Delegation]s that provide the authority to perform the enclosed [Command] | Yes | | `exp` | `Integer \| null`[^js-num] | The UTC Unix timestamp at which the Task expires | Yes | -The CID of a Task is useful for reverse lookups in [Receipt]-sharing networks to check if someone else has run this Task before, and in [UCAN Promise] to connect Tasks together. +The CID of a Task is useful for reverse look-ups in [Receipt]-sharing networks to check if someone else has run this Task before, and in [UCAN Promise] to connect Tasks together. ## 3.3 Invocation From c864cfdb07ce5301d346d2b00f0b30424544e405 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 22:24:19 -0700 Subject: [PATCH 062/127] Fix layout --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e36ca2ae..1b264620 100644 --- a/README.md +++ b/README.md @@ -240,12 +240,12 @@ If present, the REQUIRED `nnc` field MUST include a random nonce expressed in AS A Task wraps a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. -| Field | Type | Description | Required | -|-------|----------------------------|----------------------------------------------------------------------------------------------|----------| -| `run` | `&Command` | The `run` field MUST contain a link to the [Task] to be run | Yes | -| `mta` | `{String : Any}` | Extensible fields, e.g. resource limits, human-readable tags, notes, and so on | Yes | -| `prf` | `[&Delegation]` | Links to any [UCAN Delegation]s that provide the authority to perform the enclosed [Command] | Yes | -| `exp` | `Integer \| null`[^js-num] | The UTC Unix timestamp at which the Task expires | Yes | +| Field | Type | Description | Required | +|-------|----------------------------|---------------------------------------------------------------------------------|----------| +| `run` | `&Command` | The `run` field MUST contain a link to the [Task] to be run | Yes | +| `mta` | `{String : Any}` | Extensible fields, e.g. resource limits, human-readable tags, notes, and so on | Yes | +| `prf` | `[&Delegation]` | [UCAN Delegation]s that provide the authority to perform the enclosed [Command] | Yes | +| `exp` | `Integer \| null`[^js-num] | The UTC Unix timestamp at which the Task expires | Yes | The CID of a Task is useful for reverse look-ups in [Receipt]-sharing networks to check if someone else has run this Task before, and in [UCAN Promise] to connect Tasks together. From 7df4067f5c155e1ceaae7108082012abfdf257e8 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 22:26:58 -0700 Subject: [PATCH 063/127] Consistent example formats --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 1b264620..40080d7c 100644 --- a/README.md +++ b/README.md @@ -318,10 +318,10 @@ The Invocation Payload attaches sender, receiver, and provenance to the [Task]. ```js { "sig": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, - "inv": { + "inv": cid({ "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", - "run": { + "run": cid({ "act": { "nnc": "email-akiko#1234567890" "cmd": "msg/send", @@ -331,12 +331,12 @@ The Invocation Payload attaches sender, receiver, and provenance to the [Task]. "subject": "Coffee", "body": "Let get coffee sometime and talk about UCAN Invocations!" } - }, + }), "mta": {}, "prf": [{"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}], "exp": 1697409438 } - } + }) } ``` @@ -345,10 +345,10 @@ The Invocation Payload attaches sender, receiver, and provenance to the [Task]. ```js { "sig": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, - "inv": { + "inv": cid({ "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", - "run": { + "run": ({ "act": { "nnc": "", // NOTE: as stated above, idempotent Actions should always have the same nonce "act": "wasm/run", @@ -358,8 +358,8 @@ The Invocation Payload attaches sender, receiver, and provenance to the [Task]. "params": [42] } } - } - } + }) + }) } ``` From cbcb2e75da3b7fe364164e6f3032545256179164 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 23:05:48 -0700 Subject: [PATCH 064/127] Remove broken link --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 40080d7c..eff03520 100644 --- a/README.md +++ b/README.md @@ -495,7 +495,7 @@ The Object Capability Network ([OCapN]) protocol extends [CapTP] with a generali # 6 Acknowledgements -Many thanks to [Mark Miller] for his [trail blazing work][erights] on [capability systems]. +Many thanks to [Mark Miller] for his [trail blazing work][eRights] on [capability systems]. Many thanks to [Luke Marsen] and [Simon Worthington] for their feedback on invocation model from their work on [Bacalhau] and [IPVM]. @@ -576,6 +576,5 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [`magnet`]: https://en.wikipedia.org/wiki/Magnet_URI_scheme [capability systems]: https://en.wikipedia.org/wiki/Capability-based_security [distributed promise pipelines]: http://erights.org/elib/distrib/pipeline.html -[eRights]: https:/erights.org -[erights]: https://erights.org +[eRights]: https://erights.org [ucanto RPC]: https://github.com/web3-storage/ucanto From 4b73b94bca07c047a54c569eef46eaeed38fc98f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 16:38:13 -0700 Subject: [PATCH 065/127] Fix old wording --- README.md | 29 +++++++++++++++++------------ signature.ipldsch | 2 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index eff03520..5fc984bb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # UCAN Invocation Specification v1.0.0-rc. 1 +FIXME ZL: pls mention that the cmd defines a shape for arg +FIXME ZL: run field for feed-forward + ## Editors - [Brooklyn Zelenka], [Fission] @@ -217,11 +220,13 @@ Using the JavaScript analogy from the introduction, an Action is similar to wrap ### 3.1.1 Command -The Command (`cmd`) field MUST contain a concrete operation that can be sent to the Resource. This field can be thought of as the message or trait being sent to the resource. Note that _unlike_ a UCAN Delegation [Ability], which includes hierarchy, an Operation MUST be fully concrete. +The Command (`cmd`) field MUST contain a concrete, dispatchable message that can be sent to the Executor. The Command MUST define the shape of the data in the [Arguments]. + +This field can be thought of as the method, message, or trait being sent to the resource. Note that _unlike_ a UCAN Delegation [Ability], which includes hierarchy, a Command MUST be fully concrete. ### 3.1.2 Arguments -The Arguments (`arg`) field, MAY contain any parameters expected by the Resource/Operation pair, which MAY be different between different Resources and Operations, and is thus left to the executor to define the shape of this data. This field MUST be representable as a map or keyword list. +The Arguments (`arg`) field, MAY contain any parameters expected by the Command. The Subject MUST be considered the authority on the shape of this data. This field MUST be representable as a map or keyword list. UCAN capabilities provided in proofs MAY impose certain constraint on the type of Arguments allowed. @@ -240,18 +245,18 @@ If present, the REQUIRED `nnc` field MUST include a random nonce expressed in AS A Task wraps a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. -| Field | Type | Description | Required | -|-------|----------------------------|---------------------------------------------------------------------------------|----------| -| `run` | `&Command` | The `run` field MUST contain a link to the [Task] to be run | Yes | -| `mta` | `{String : Any}` | Extensible fields, e.g. resource limits, human-readable tags, notes, and so on | Yes | -| `prf` | `[&Delegation]` | [UCAN Delegation]s that provide the authority to perform the enclosed [Command] | Yes | -| `exp` | `Integer \| null`[^js-num] | The UTC Unix timestamp at which the Task expires | Yes | +| Field | Type | Required | Description | +|-------|----------------------------|----------|--------------------------------------------------------------------------------| +| `act` | `&Command` | Yes | The CID of the [Task] to be run | +| `mta` | `{String : Any}` | Yes | Extensible fields, e.g. resource limits, human-readable tags, notes, and so on | +| `prf` | `[&Delegation]` | Yes | [UCAN Delegation]s that provide the authority to perform the `act` [Action] | +| `exp` | `Integer \| null`[^js-num] | Yes | The UTC Unix timestamp at which the Task expires | The CID of a Task is useful for reverse look-ups in [Receipt]-sharing networks to check if someone else has run this Task before, and in [UCAN Promise] to connect Tasks together. ## 3.3 Invocation -As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is an instruction to the [Executor] to perform enclosed [Task]. [Invocation Payload]s are not executable until they have been signed and [Delegation] proofs validated. +As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is a request to the [Executor] to perform the enclosed [Task]. [Invocation Payload]s are not executable until they have been signed and [Delegation] proofs validated. ### 3.3.1 Invocation Payload @@ -351,7 +356,7 @@ The Invocation Payload attaches sender, receiver, and provenance to the [Task]. "run": ({ "act": { "nnc": "", // NOTE: as stated above, idempotent Actions should always have the same nonce - "act": "wasm/run", + "cmd": "wasm/run", "arg": { "mod": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", "fun": "add_one", @@ -406,7 +411,7 @@ Receipts MUST use the same-or-higher version number as the Invocation that they | `out` | `Result` | Yes | MUST contain the value output of the invocation in [Result] format | | `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | | `mta` | `{String : Any}` | Yes | Additional data about the receipt | -| `rec` | `&Receipt` | No | Recursive `Signed`s if the Invocation was proxied to another Executor | +| `rec` | `&Receipt` | No | Recursive `Receipt`s if the Invocation was proxied to another Executor | | `iat` | `Integer | null`[^js-num] | No | The UTC Unix timestand at which the Receipt was issed | A few of these fields warrant further comment below. @@ -456,7 +461,7 @@ const receipt = { } ``` -## 4.2 Receipt Envelope +## 4.2 Receipt (Envelope) | Field | Type | Required | Description | |-------|-------------------|----------|--------------------------------------------------------------------------| diff --git a/signature.ipldsch b/signature.ipldsch index 34811b61..fc4e7118 100644 --- a/signature.ipldsch +++ b/signature.ipldsch @@ -1,6 +1,6 @@ type Signature union { | inline Bytes - | batch BatchSignature + | batch &BatchSignature } type BatchSignature struct { From 06f858d0091061df388b494f5ec734ee5f661b30 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 16:44:45 -0700 Subject: [PATCH 066/127] Remove unused markdown linter --- .github/workflows/linkcheck.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/linkcheck.yml b/.github/workflows/linkcheck.yml index 10fa5324..b5a0a324 100644 --- a/.github/workflows/linkcheck.yml +++ b/.github/workflows/linkcheck.yml @@ -17,6 +17,3 @@ jobs: check-modified-files-only: "yes" base-branch: "main" config-file: "./.github/workflows/linkcheck.cfg.json" - - uses: DavidAnson/markdownlint-cli2-action@v9 - with: - globs: "README.md" From 070876a3d97e4bfefb87e77730eca66f6ffb2841 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 16:55:45 -0700 Subject: [PATCH 067/127] Improve storytelling on Reciepts --- README.md | 79 ++++++++++++++++++++++++++----------------------------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 5fc984bb..737117f3 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,5 @@ # UCAN Invocation Specification v1.0.0-rc. 1 -FIXME ZL: pls mention that the cmd defines a shape for arg -FIXME ZL: run field for feed-forward - ## Editors - [Brooklyn Zelenka], [Fission] @@ -370,7 +367,36 @@ The Invocation Payload attaches sender, receiver, and provenance to the [Task]. # 4 Response -## 4.1 Result +## 4.1 Receipt (Envelope) + +A `Receipt` is an attestation of the [Result Payload]. A Receipt MUST be signed by the [Executor] (including by [Execution Proxy]). + +**NB: a Receipt does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. + +Receipts MUST use the same-or-higher version number as the [Invocation] that they reference. + +| Field | Type | Required | Description | +|-------|-------------------|----------|--------------------------------------------------------------------------| +| `ucr` | `&ReceiptPayload` | Yes | The CID for the [Receipt Payload] | +| `sig` | `Signature` | Yes | The [Signature] of the `uci` value, by the [Receipt Payload]'s `iss` DID | + +## 4.2 Receipt Payload + +| Field | Type | Required | Description | +|-------|---------------------------|----------|------------------------------------------------------------------------------------| +| `ucr` | `SemVer` | Yes | The version of this spec that the Receipt conforms to | +| `iss` | `DID` | Yes | The DID of the Executor | +| `ran` | `&Invocation` | Yes | MUST be a link to the [Invocation] that the Receipt is for | +| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | +| `out` | `Result` | Yes | MUST contain the value output of the invocation in [Result] format | +| `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | +| `mta` | `{String : Any}` | Yes | Additional data about the receipt | +| `rec` | `&Receipt` | No | Recursive `Receipt`s if the Invocation was proxied to another Executor | +| `iat` | `Integer | null`[^js-num] | No | The UTC Unix timestand at which the Receipt was issed | + +A few of these fields warrant further comment below. + +### 4.2.1 Result A Result records the output of the [Task], as well as its success or failure state. A Result MUST be formatted as map with a single `tag` field. @@ -392,31 +418,7 @@ A Result records the output of the [Task], as well as its success or failure sta } ``` -# 4.2 Receipt - -A `Receipt` is an attestation of the [Result] and requested [enqueued Tasks][enqueue]. A Receipt MUST be signed by the [Executor] (including by [Execution Proxy]). - -**NB: a Receipt does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. - -Receipts MUST use the same-or-higher version number as the Invocation that they reference. - -### 4.1.1 Receipt Payload - -| Field | Type | Required | Description | -|-------|---------------------------|----------|------------------------------------------------------------------------------------| -| `ucr` | `SemVer` | Yes | The version of this spec that the Receipt conforms to | -| `iss` | `DID` | Yes | The DID of the Executor | -| `ran` | `&Invocation` | Yes | MUST be a link to the [Invocation] that the Receipt is for | -| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | -| `out` | `Result` | Yes | MUST contain the value output of the invocation in [Result] format | -| `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | -| `mta` | `{String : Any}` | Yes | Additional data about the receipt | -| `rec` | `&Receipt` | No | Recursive `Receipt`s if the Invocation was proxied to another Executor | -| `iat` | `Integer | null`[^js-num] | No | The UTC Unix timestand at which the Receipt was issed | - -A few of these fields warrant further comment below. - -### 4.1.1.1 Enqueue +### 4.2.2 Enqueue The result of an [Invocation] MAY include a request for further actions to be performed. This is a process of requesting that the invoker "enqueue" a new Task. This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. @@ -424,7 +426,7 @@ Enqueued [Task]s describe requests for future work to be performed. They SHOULD All [Tasks]s in an [enqueue] array MUST be treated as concurrent, unless explicit data dependencies between them exist via promise [Pipeline]s. -### 4.1.1.2 Proxy Execution +### 4.2.3 Proxy Execution If the Receipt Issuer is not identical to the `aud` field of Invocation referenced in the `ran` field, a [Delegation] proof chain SHOULD be included. If a chain is present, it MUST show that such a proxy execution was authorized by the original listed `aud` Agent. @@ -461,13 +463,6 @@ const receipt = { } ``` -## 4.2 Receipt (Envelope) - -| Field | Type | Required | Description | -|-------|-------------------|----------|--------------------------------------------------------------------------| -| `ucr` | `&ReceiptPayload` | Yes | The CID for the [Receipt Payload] | -| `sig` | `Signature` | Yes | The [Signature] of the `uci` value, by the [Receipt Payload]'s `iss` DID | - ## 4.3 DAG-JSON Examples ``` js @@ -524,20 +519,20 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Arguments]: #312-arguments [Command]: #311-command -[Execution Proxy]: #41112-proxy-execution +[Execution Proxy]: #423-proxy-execution [Executor]: #212-executor [Invocation Payload]: #331-invocation-payload [Invocation Signature]: #331-invocation-envelope [Invocation]: #33-invocation [Invoker]: #211-invoker [Nonce]: #314-nonce -[Receipt Payload]: #411-receipt-payload -[Receipt]: #42-receipt +[Receipt Payload]: #42-receipt-payload +[Receipt]: #41-receipt-envelope [Response]: #4-response -[Result]: #41-result +[Result]: #421-result [Subject]: #313-subject [Task]: #32-task -[enqueue]: #4111-enqueue +[enqueue]: #422-enqueue [lazy-vs-eager]: #112-lazy-vs-eager-evaluation From ab960810c41340623808ce073d1a53f567eba066 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 16:57:33 -0700 Subject: [PATCH 068/127] Missing link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 737117f3..7137dd09 100644 --- a/README.md +++ b/README.md @@ -517,6 +517,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen +[Action]: #31-action [Arguments]: #312-arguments [Command]: #311-command [Execution Proxy]: #423-proxy-execution From ff98d47d2aeb350bc238d6c478324162c103dbfe Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 16:59:24 -0700 Subject: [PATCH 069/127] Fix broken table formatting --- README.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 7137dd09..430c2685 100644 --- a/README.md +++ b/README.md @@ -382,17 +382,19 @@ Receipts MUST use the same-or-higher version number as the [Invocation] that the ## 4.2 Receipt Payload -| Field | Type | Required | Description | -|-------|---------------------------|----------|------------------------------------------------------------------------------------| -| `ucr` | `SemVer` | Yes | The version of this spec that the Receipt conforms to | -| `iss` | `DID` | Yes | The DID of the Executor | -| `ran` | `&Invocation` | Yes | MUST be a link to the [Invocation] that the Receipt is for | -| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | -| `out` | `Result` | Yes | MUST contain the value output of the invocation in [Result] format | -| `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | -| `mta` | `{String : Any}` | Yes | Additional data about the receipt | -| `rec` | `&Receipt` | No | Recursive `Receipt`s if the Invocation was proxied to another Executor | -| `iat` | `Integer | null`[^js-num] | No | The UTC Unix timestand at which the Receipt was issed | +Receipt Payloads MUST conform to the following shape: + +| Field | Type | Required | Description | +|-------|--------------------|----------|------------------------------------------------------------------------------------| +| `ucr` | `SemVer` | Yes | The version of this spec that the Receipt conforms to | +| `iss` | `DID` | Yes | The DID of the Executor | +| `ran` | `&Invocation` | Yes | A link to the [Invocation] that the Receipt is for | +| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | +| `out` | `Result` | Yes | The value output of the invocation in [Result] format | +| `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | +| `mta` | `{String : Any}` | Yes | Additional data about the receipt | +| `rec` | `&Receipt` | No | Recursive `Receipt`s if the Invocation was proxied to another Executor | +| `iat` | `Integer`[^js-num] | No | The UTC Unix timestand at which the Receipt was issed | A few of these fields warrant further comment below. From 4280122f6e31e49a5d29a5002d13d6ad121e1be1 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 17:07:32 -0700 Subject: [PATCH 070/127] Spelling & dictionary --- .github/workflows/words-to-ignore.txt | 1 + README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 9ccdbf7e..2ca2da0c 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -65,6 +65,7 @@ dataflow de delegator dholms +dispatchable enqueuing expede facto diff --git a/README.md b/README.md index 430c2685..1e20fb73 100644 --- a/README.md +++ b/README.md @@ -394,7 +394,7 @@ Receipt Payloads MUST conform to the following shape: | `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | | `mta` | `{String : Any}` | Yes | Additional data about the receipt | | `rec` | `&Receipt` | No | Recursive `Receipt`s if the Invocation was proxied to another Executor | -| `iat` | `Integer`[^js-num] | No | The UTC Unix timestand at which the Receipt was issed | +| `iat` | `Integer`[^js-num] | No | The UTC Unix timestamp at which the Receipt was issued | A few of these fields warrant further comment below. From c5b26b7b361566bd271d90b47a7fe0264841508b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 17:21:04 -0700 Subject: [PATCH 071/127] more pretty pictures --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 1e20fb73..027c4a52 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,28 @@ Consider the following fictitious scenario: Akiko is going away for the weekend. Her good friend Boris is going to borrow her car while she's away. They meet at a nearby cafe, and Akiko hands Boris her car keys. Boris now has the capability to drive Akiko's car whenever he wants to. Depending on their plans for the rest of the day, Akiko may find Boris quite rude if he immediately leaves the cafe to go for a drive. On the other hand, if Akiko asks Boris to run some last minute pre-vacation errands for that require a car, she may expect Boris to immediately drive off. +``` mermaid +sequenceDiagram + participant 🚗 + actor Akiko + actor Boris + + autonumber + + Note over 🚗, Akiko: Akiko buys a car + 🚗 -->> Akiko: Delegate(Drive 🚗) + + Note over Akiko, Boris: Boris offers to run errands for Akiko + Boris -->> Akiko: Delegate(Boris to run errands) + + Note over Akiko, Boris: Akiko give Boris access to her car + Akiko -->> Boris: Delegate(Drive 🚗) + + Note over 🚗, Boris: Akiko asks Boris to use her car to run errands + Akiko ->> Boris: Invoke!(Boris to run errands, using 🚗) + Boris ->> 🚗: Invoke!(Drive 🚗) +``` + ## 1.1.2 Lazy vs Eager Evaluation In a referentially transparent setting, the description of a task is equivalent to having done so: a function and its results are interchangeable. [Programming languages with call-by-need semantics][Haskell] have shown that this can be an elegant programming model, especially for pure functions. However, _when_ something will run can sometimes be unclear. From f64f8f387a7f0cd60856e11ee29a1f0f419c685a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 17:56:45 -0700 Subject: [PATCH 072/127] Fixup car diagram --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 027c4a52..89f98be6 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,8 @@ Consider the following fictitious scenario: Akiko is going away for the weekend. Her good friend Boris is going to borrow her car while she's away. They meet at a nearby cafe, and Akiko hands Boris her car keys. Boris now has the capability to drive Akiko's car whenever he wants to. Depending on their plans for the rest of the day, Akiko may find Boris quite rude if he immediately leaves the cafe to go for a drive. On the other hand, if Akiko asks Boris to run some last minute pre-vacation errands for that require a car, she may expect Boris to immediately drive off. +To put this in terms closer to a UCAN flow: + ``` mermaid sequenceDiagram participant 🚗 @@ -56,19 +58,21 @@ sequenceDiagram autonumber Note over 🚗, Akiko: Akiko buys a car - 🚗 -->> Akiko: Delegate(Drive 🚗) + 🚗 -->> Akiko: Delegate(Drive 🚗) Note over Akiko, Boris: Boris offers to run errands for Akiko Boris -->> Akiko: Delegate(Boris to run errands) - Note over Akiko, Boris: Akiko give Boris access to her car + Note over Akiko, Boris: Akiko gives Boris access to her car Akiko -->> Boris: Delegate(Drive 🚗) Note over 🚗, Boris: Akiko asks Boris to use her car to run errands - Akiko ->> Boris: Invoke!(Boris to run errands, using 🚗) + Akiko ->> Boris: Invoke!(Boris to run errands, using 🚗 (➌)) Boris ->> 🚗: Invoke!(Drive 🚗) ``` +In the example above, steps ➌ and ➍ are qualitatively different. Step ➌ grants authority (to drive the car). ➍ is a _command_ to do so. + ## 1.1.2 Lazy vs Eager Evaluation In a referentially transparent setting, the description of a task is equivalent to having done so: a function and its results are interchangeable. [Programming languages with call-by-need semantics][Haskell] have shown that this can be an elegant programming model, especially for pure functions. However, _when_ something will run can sometimes be unclear. From bdf90c4d8c03bbae88bd72c841908f820574cf37 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 17:59:35 -0700 Subject: [PATCH 073/127] Remove gossip section (moved to different section) --- README.md | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 89f98be6..5d5b8ad4 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,10 @@ sequenceDiagram Boris ->> 🚗: Invoke!(Drive 🚗) ``` -In the example above, steps ➌ and ➍ are qualitatively different. Step ➌ grants authority (to drive the car). ➍ is a _command_ to do so. +In the example above, steps ➌ and ➍ are qualitatively different: + +- Step ➌ grants authority (to drive the car) +- Step ➍ is a _command_ to do so ## 1.1.2 Lazy vs Eager Evaluation @@ -93,19 +96,11 @@ Delegating a capability is like the statement `message`. Task is akin to `messag However, there is clearly a distinction between passing a function and invoking it. The same is true for capabilities: delegating the authority to do something is not the same as asking for it to be done immediately, even if sometimes it's clear from context. -## 1.2 Delegation Gossip - -UCAN delegation can be gossiped freely between services. This is not true for invocation. - -For example, if `alice@example.com` delegates her `web3.storage` storage quota to `bob@example.com`, it may be beneficial for all of the related `web3.storage` services to cache this information. If this were to be understood as an invocation, then gossiping this information would lead to validation failures due to principal misalignment in the certificate chain. - -By distinguishing invocation from delegation, agents are able to understand the user intention, and handle such messages accordingly. Receipt of an invocation with misaligned principles will fail, but a delegation may be held in e.g. Bob's proxy inbox to be acted on when he comes online or widely distributed across the `web3.storage` infrastructure. - -## 1.3 Public Resources +## 1.2 Public Resources A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard required for initiating flow (e.g. sign up). These cases typically involve a reference passed out of band (such as a web link). Due to [designation without authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain , but this SHOULD NOT be the default behavior. -## 1.4 Promise Pipelining +## 1.3 Promise Pipelining [UCAN Promise] extends UCAN Invocation with [distributed promise pipelines]. Promises are helpful in a wide variety of situations for efficiency and convenience. Implementing [UCAN Promise]s is RECOMMENDED. From 42636c3fea84882efb0320fec69864c13f31ad1b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:05:50 -0700 Subject: [PATCH 074/127] Fix link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5d5b8ad4..bcbbbde4 100644 --- a/README.md +++ b/README.md @@ -390,7 +390,7 @@ The Invocation Payload attaches sender, receiver, and provenance to the [Task]. ## 4.1 Receipt (Envelope) -A `Receipt` is an attestation of the [Result Payload]. A Receipt MUST be signed by the [Executor] (including by [Execution Proxy]). +A `Receipt` is an attestation of the [Result Payload]. A Receipt MUST be signed by the [Executor] (including by [Proxy Execution]). **NB: a Receipt does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. @@ -465,7 +465,7 @@ const delegation = { const workerDelegation = { iss: "did:web:example.com", - aud: "did:web:worker.not-example.net" + aud: "did:web:worker.not-example.net", sub: "did:web:example.com", can: "ucan/execute", // ... From 97627e07ff85bb4b39549ff3c57b8b5c75cf3510 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:06:43 -0700 Subject: [PATCH 075/127] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bcbbbde4..add64461 100644 --- a/README.md +++ b/README.md @@ -390,7 +390,7 @@ The Invocation Payload attaches sender, receiver, and provenance to the [Task]. ## 4.1 Receipt (Envelope) -A `Receipt` is an attestation of the [Result Payload]. A Receipt MUST be signed by the [Executor] (including by [Proxy Execution]). +A `Receipt` is an attestation of the [Result Payload]. A Receipt MUST be signed by the [Executor] (including by [Execution Proxy]). **NB: a Receipt does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. From 214079190b3f23f22028203eab03c096750f316a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:07:54 -0700 Subject: [PATCH 076/127] Fix code formatting --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index add64461..38789cdc 100644 --- a/README.md +++ b/README.md @@ -488,10 +488,10 @@ const receipt = { ## 4.3 DAG-JSON Examples -``` js +``` json { "ucr": { - "ran": {"/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu"} + "ran": {"/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu"}, "out": { "ok": ["bob@example.com", "alice@example.com"] }, @@ -500,7 +500,7 @@ const receipt = { "total-time": [400, "hours"] } }, - "sig": {"/": {bytes: "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU"}} + "sig": {"/": {"bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU"}} } ``` From eb148d141b1cd1d0dc4f0ff03342add4ebed77a9 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:09:03 -0700 Subject: [PATCH 077/127] Remove overzealous link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 38789cdc..83d63d91 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ A core part of UCAN's design is interacting with the wider, non-UCAN world. Many ## 1.3 Promise Pipelining -[UCAN Promise] extends UCAN Invocation with [distributed promise pipelines]. Promises are helpful in a wide variety of situations for efficiency and convenience. Implementing [UCAN Promise]s is RECOMMENDED. +[UCAN Promise] extends UCAN Invocation with [distributed promise pipelines]. Promises are helpful in a wide variety of situations for efficiency and convenience. Implementing UCAN Promises is RECOMMENDED. # 2 Concepts From d3f9eb13ff2ba6aa79a4424be54f4fa874902d3a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:15:39 -0700 Subject: [PATCH 078/127] Add principal alignment diagram --- README.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 83d63d91..9e50b919 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ However, there is clearly a distinction between passing a function and invoking ## 1.2 Public Resources -A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard required for initiating flow (e.g. sign up). These cases typically involve a reference passed out of band (such as a web link). Due to [designation without authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain , but this SHOULD NOT be the default behavior. +A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard required for initiating flow (e.g. sign up). These cases typically involve a reference passed out of band (such as a web link). Due to [designation without authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain, but this SHOULD NOT be the default behavior. ## 1.3 Promise Pipelining @@ -111,7 +111,7 @@ A core part of UCAN's design is interacting with the wider, non-UCAN world. Many Task adds two new roles to UCAN: invoker and executor. The existing UCAN delegator and delegate principals MUST persist to the invocation. | UCAN Field | Delegation | Invocation | -| ---------- | -------------------------------------- | ------------------------------- | +|------------|----------------------------------------|---------------------------------| | `iss` | Delegator: transfer authority (active) | Invoker: request task (active) | | `aud` | Delegate: gain authority (passive) | Executor: perform task (active) | @@ -272,11 +272,77 @@ A Task wraps a [Command] with contextual information. This includes expiration t The CID of a Task is useful for reverse look-ups in [Receipt]-sharing networks to check if someone else has run this Task before, and in [UCAN Promise] to connect Tasks together. +### 3.2.1 Proof Chains + +A Task MUST include the entire [UCAN Delegation] proof chain in the `prf` field. The chain MUST form a direct line of authority, starting with the delegation with an `aud` that matches the Invoker, and ending with a delegation where the `iss` matches the `sub`. The `sub` throughout MUST match the `aud` of the Invocation. + +``` mermaid +flowchart RL + invoker((    Dan    )) + subject((    Alice    )) + + subject -- controls --> resource[(Storage)] + rootCap -- references --> resource + + subgraph Delegations + subgraph root [Root UCAN] + subgraph rooting [Root Issuer] + rootIss(iss: Alice) + rootSub(sub: Alice) + end + + rootCap("cap: (Storage, crud/*)") + rootAud(aud: Bob) + end + + subgraph del1 [Delegated UCAN] + del1Iss(iss: Bob) --> rootAud + del1Sub(sub: Alice) + del1Aud(aud: Carol) + del1Cap("cap: (Storage, crud/*)") --> rootCap + + + del1Sub --> rootSub + end + + subgraph del2 [Delegated UCAN] + del2Iss(iss: Carol) --> del1Aud + del2Sub(sub: Alice) + del2Aud(aud: Dan) + del2Cap("cap: (Storage, crud/*)") --> del1Cap + + del2Sub --> del1Sub + end + end + + subgraph inv [Invocation] + invIss(iss: Dan) + args("args: [Storage, crud/update, (key, value)]") + invSub(sub: Alice) + prf("proofs") + end + + invIss --> del2Aud + invoker --> invIss + args --> del2Cap + invSub --> del2Sub + rootIss --> subject + rootSub --> subject + prf --> Delegations +``` + ## 3.3 Invocation As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is a request to the [Executor] to perform the enclosed [Task]. [Invocation Payload]s are not executable until they have been signed and [Delegation] proofs validated. -### 3.3.1 Invocation Payload +### 3.3.1 Invocation (Envelope) + +| Field | Type | Required | Description | +|-------|----------------------|----------|----------------------------------------------------------| +| `uci` | `&InvocationPayload` | Yes | The CID of the [Invocation Payload] | +| `sig` | `&Signature` | Yes | A signature by the Payload's `iss` over the CID in `uci` | + +### 3.3.2 Invocation Payload The Invocation Payload attaches sender, receiver, and provenance to the [Task]. @@ -287,13 +353,6 @@ The Invocation Payload attaches sender, receiver, and provenance to the [Task]. | `run` | `&Task` | Yes | The enclosed [Task]'s CID | | `cau` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | -### 3.3.2 Invocation (Envelope) - -| Field | Type | Required | Description | -|-------|----------------------|----------|----------------------------------------------------------| -| `uci` | `&InvocationPayload` | Yes | The CID of the [Invocation Payload] | -| `sig` | `&Signature` | Yes | A signature by the Payload's `iss` over the CID in `uci` | - ## 3.4 Examples ### 3.4.1 Interacting with an HTTP API @@ -602,3 +661,4 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [distributed promise pipelines]: http://erights.org/elib/distrib/pipeline.html [eRights]: https://erights.org [ucanto RPC]: https://github.com/web3-storage/ucanto + From 393dcdefd9cbcee05fd9c393c19a034666237ef6 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:18:18 -0700 Subject: [PATCH 079/127] Add IPLD note --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9e50b919..2311d5f6 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ ## Depends On -- [DAG-CBOR] +- [IPLD] - [DID] - [UCAN Delegation] @@ -104,6 +104,10 @@ A core part of UCAN's design is interacting with the wider, non-UCAN world. Many [UCAN Promise] extends UCAN Invocation with [distributed promise pipelines]. Promises are helpful in a wide variety of situations for efficiency and convenience. Implementing UCAN Promises is RECOMMENDED. +## 1.4 Serialization + +UCAN Invocations MUST be encoded with some [IPLD] codec. [DAG-CBOR] is RECOMMENDED. + # 2 Concepts ## 2.1 Roles @@ -636,6 +640,8 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Electronic Rights Transfer Protocol (ERTP)]: https://docs.agoric.com/guides/ertp/ [Fission]: https://fission.codes/ [Haskell]: https://en.wikipedia.org/wiki/Haskell +[IEEE-754]: https://ieeexplore.ieee.org/document/8766229 +[IPLD]: https://ipld.io/ [IPVM]: https://github.com/ipvm-wg [Irakli Gozalishvili]: https://github.com/Gozala [JS Number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number @@ -661,4 +667,3 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [distributed promise pipelines]: http://erights.org/elib/distrib/pipeline.html [eRights]: https://erights.org [ucanto RPC]: https://github.com/web3-storage/ucanto - From b704740e74b1d069812c59b103170eb6cc62d7a9 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:20:31 -0700 Subject: [PATCH 080/127] Fix diagram --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2311d5f6..02d52ae3 100644 --- a/README.md +++ b/README.md @@ -295,7 +295,7 @@ flowchart RL rootSub(sub: Alice) end - rootCap("cap: (Storage, crud/*)") + rootCap("(Storage, crud/*)") rootAud(aud: Bob) end @@ -303,8 +303,7 @@ flowchart RL del1Iss(iss: Bob) --> rootAud del1Sub(sub: Alice) del1Aud(aud: Carol) - del1Cap("cap: (Storage, crud/*)") --> rootCap - + del1Cap("(Storage, crud/*)") --> rootCap del1Sub --> rootSub end @@ -313,7 +312,7 @@ flowchart RL del2Iss(iss: Carol) --> del1Aud del2Sub(sub: Alice) del2Aud(aud: Dan) - del2Cap("cap: (Storage, crud/*)") --> del1Cap + del2Cap("(Storage, crud/*)") --> del1Cap del2Sub --> del1Sub end @@ -322,7 +321,7 @@ flowchart RL subgraph inv [Invocation] invIss(iss: Dan) args("args: [Storage, crud/update, (key, value)]") - invSub(sub: Alice) + invSub(aud: Alice) prf("proofs") end From 01920f2c696aacfbe3ec41418e615264636607d9 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:22:49 -0700 Subject: [PATCH 081/127] Move diagram --- README.md | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 02d52ae3..9c1fc906 100644 --- a/README.md +++ b/README.md @@ -276,7 +276,29 @@ A Task wraps a [Command] with contextual information. This includes expiration t The CID of a Task is useful for reverse look-ups in [Receipt]-sharing networks to check if someone else has run this Task before, and in [UCAN Promise] to connect Tasks together. -### 3.2.1 Proof Chains +## 3.3 Invocation + +As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is a request to the [Executor] to perform the enclosed [Task]. [Invocation Payload]s are not executable until they have been signed and [Delegation] proofs validated. + +### 3.3.1 Invocation (Envelope) + +| Field | Type | Required | Description | +|-------|----------------------|----------|----------------------------------------------------------| +| `uci` | `&InvocationPayload` | Yes | The CID of the [Invocation Payload] | +| `sig` | `&Signature` | Yes | A signature by the Payload's `iss` over the CID in `uci` | + +### 3.3.2 Invocation Payload + +The Invocation Payload attaches sender, receiver, and provenance to the [Task]. + +| Field | Type | Required | Description | +|-------|------------|----------|-----------------------------------------------------------| +| `iss` | `DID` | Yes | The DID of the [Invoker] | +| `aud` | `DID` | Yes | The DID of the intended [Executor] | +| `run` | `&Task` | Yes | The enclosed [Task]'s CID | +| `cau` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | + +## 3.4 Proof Chains A Task MUST include the entire [UCAN Delegation] proof chain in the `prf` field. The chain MUST form a direct line of authority, starting with the delegation with an `aud` that matches the Invoker, and ending with a delegation where the `iss` matches the `sub`. The `sub` throughout MUST match the `aud` of the Invocation. @@ -334,31 +356,9 @@ flowchart RL prf --> Delegations ``` -## 3.3 Invocation - -As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is a request to the [Executor] to perform the enclosed [Task]. [Invocation Payload]s are not executable until they have been signed and [Delegation] proofs validated. - -### 3.3.1 Invocation (Envelope) - -| Field | Type | Required | Description | -|-------|----------------------|----------|----------------------------------------------------------| -| `uci` | `&InvocationPayload` | Yes | The CID of the [Invocation Payload] | -| `sig` | `&Signature` | Yes | A signature by the Payload's `iss` over the CID in `uci` | - -### 3.3.2 Invocation Payload - -The Invocation Payload attaches sender, receiver, and provenance to the [Task]. - -| Field | Type | Required | Description | -|-------|------------|----------|-----------------------------------------------------------| -| `iss` | `DID` | Yes | The DID of the [Invoker] | -| `aud` | `DID` | Yes | The DID of the intended [Executor] | -| `run` | `&Task` | Yes | The enclosed [Task]'s CID | -| `cau` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | - -## 3.4 Examples +## 3.5 Examples -### 3.4.1 Interacting with an HTTP API +### 3.5.1 Interacting with an HTTP API ```js { @@ -398,7 +398,7 @@ The Invocation Payload attaches sender, receiver, and provenance to the [Task]. } ``` -### 3.4.2 Sending Email +### 3.5.2 Sending Email ```js { @@ -425,7 +425,7 @@ The Invocation Payload attaches sender, receiver, and provenance to the [Task]. } ``` -### 3.4.3 Inline WebAssembly +### 3.5.3 Inline WebAssembly ```js { From e7a2915e3a417f5d8883455431c6e643fd3c4822 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:24:12 -0700 Subject: [PATCH 082/127] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c1fc906..dcb5aef0 100644 --- a/README.md +++ b/README.md @@ -452,7 +452,7 @@ flowchart RL ## 4.1 Receipt (Envelope) -A `Receipt` is an attestation of the [Result Payload]. A Receipt MUST be signed by the [Executor] (including by [Execution Proxy]). +A `Receipt` is an attestation of the [Receipt Payload]. A Receipt MUST be signed by the [Executor] (including by [Execution Proxy]). **NB: a Receipt does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. From 50dbcd1c8891db0e64a27f4ff6f754fc57bec45e Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:26:34 -0700 Subject: [PATCH 083/127] Fix example typos --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dcb5aef0..5731d826 100644 --- a/README.md +++ b/README.md @@ -433,7 +433,10 @@ flowchart RL "inv": cid({ "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", - "run": ({ + "run": cid({ + "mta": { + "fuel": 999999 + }, "act": { "nnc": "", // NOTE: as stated above, idempotent Actions should always have the same nonce "cmd": "wasm/run", From ff4ee0bdf3b0d4475dfd9de1cd7eb41a4b195d95 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:27:28 -0700 Subject: [PATCH 084/127] Consistency --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5731d826..7063a6a4 100644 --- a/README.md +++ b/README.md @@ -551,7 +551,7 @@ const receipt = { } ``` -## 4.3 DAG-JSON Examples +## 4.3 Examples ``` json { From 6f0e0a618a2e418fc1687d8d1953e5a5a51d3adc Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:28:26 -0700 Subject: [PATCH 085/127] Update dict --- .github/workflows/words-to-ignore.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 2ca2da0c..c5473ba0 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -59,6 +59,7 @@ auth backend base64url canonicalized +codec cryptographic cryptographically dataflow From b8ca121b7709a540d7509e2b94e5c8855289af63 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:35:17 -0700 Subject: [PATCH 086/127] MIssing link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7063a6a4..fe239e3e 100644 --- a/README.md +++ b/README.md @@ -632,6 +632,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Bacalhau]: https://www.bacalhau.org/ [Blaine Cook]: https://github.com/blaine [Brooklyn Zelenka]: https://github.com/expede/ +[Cap 'n Proto RPC]: https://capnproto.org/ [CapTP]: https://capnproto.org/rpc.html#specification [Christine Lemmer-Webber]: https://github.com/cwebber [DAG House]: https://dag.house From 8116284913a83a61dcfaf9f29a781d4706c675a1 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:36:37 -0700 Subject: [PATCH 087/127] More missing links! --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fe239e3e..ba248c4a 100644 --- a/README.md +++ b/README.md @@ -573,7 +573,7 @@ const receipt = { [ucanto RPC] from [DAG House] is a production system that uses UCAN as the basis for an RPC layer. -The [Capability Transport Protocol (CapTP)] is one of the most influential object-capability systems, and forms the basis for much of the rest of the items on this list. +The Capability Transport Protocol ([CapTP]) is one of the most influential object-capability systems, and forms the basis for much of the rest of the items on this list. The Object Capability Network ([OCapN]) protocol extends [CapTP] with a generalized networking layer. It has implementations from the [Spritely Institute] and [Agoric]. At time of writing, it is in the process of being standardized. @@ -633,7 +633,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Blaine Cook]: https://github.com/blaine [Brooklyn Zelenka]: https://github.com/expede/ [Cap 'n Proto RPC]: https://capnproto.org/ -[CapTP]: https://capnproto.org/rpc.html#specification +[CapTP]: http://erights.org/elib/distrib/captp/index.html [Christine Lemmer-Webber]: https://github.com/cwebber [DAG House]: https://dag.house [DAG-CBOR]: https://ipld.io/specs/codecs/dag-cbor/spec/ From 2b8ed2a3fc6d47169907c14928702f3d03e0fc43 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 18:42:06 -0700 Subject: [PATCH 088/127] Missing link --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ba248c4a..0daf43d2 100644 --- a/README.md +++ b/README.md @@ -512,7 +512,7 @@ The result of an [Invocation] MAY include a request for further actions to be pe Enqueued [Task]s describe requests for future work to be performed. They SHOULD come with [Delegation]s, but MAY be a simple request back to the Invoker. -All [Tasks]s in an [enqueue] array MUST be treated as concurrent, unless explicit data dependencies between them exist via promise [Pipeline]s. +All [Task]s in an [enqueue] array MUST be treated as concurrent, unless explicit data dependencies between them exist via [UCAN Promise]s. ### 4.2.3 Proxy Execution @@ -591,7 +591,7 @@ Thanks to [Marc-Antoine Parent] for his discussions of the distinction between d Many thanks to [Quinn Wilton] for her discussion of speech acts, the dangers of signing canonicalized data, and ergonomics. -Thanks to [Blaine Cook] for sharing their experiences with OAuth 1, irreversible design decisions, and advocating for keeping the spec simple-but-evolvable. +Thanks to [Blaine Cook] for sharing their experiences with [OAuth 1], irreversible design decisions, and advocating for keeping the spec simple-but-evolvable. Thanks to [Philipp Krüger] for the enthusiastic feedback on the overall design and encoding. @@ -651,6 +651,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Luke Marsen]: https://github.com/lukemarsden [Marc-Antoine Parent]: https://github.com/maparent [Mark Miller]: https://github.com/erights +[OAuth 1]: https://oauth.net/1/ [OCapN]: https://github.com/ocapn/ [Philipp Krüger]: https://github.com/matheus23/ [Quinn Wilton]: https://github.com/QuinnWilton @@ -667,6 +668,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [`ipfs`]: https://docs.ipfs.tech/how-to/address-ipfs-on-web/#native-urls [`magnet`]: https://en.wikipedia.org/wiki/Magnet_URI_scheme [capability systems]: https://en.wikipedia.org/wiki/Capability-based_security +[designation without authorization]: https://srl.cs.jhu.edu/pubs/SRL2003-02.pdf [distributed promise pipelines]: http://erights.org/elib/distrib/pipeline.html [eRights]: https://erights.org [ucanto RPC]: https://github.com/web3-storage/ucanto From e246c3b364381a46bdf244269d4d6b7c63a61296 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 17 Oct 2023 11:02:16 -0700 Subject: [PATCH 089/127] Add ucan/* example --- README.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/README.md b/README.md index 0daf43d2..b236d4e0 100644 --- a/README.md +++ b/README.md @@ -356,6 +356,63 @@ flowchart RL prf --> Delegations ``` +### 3.4.1 Proof Paths with `ucan/*` + +Beyond [attenuation], [`ucan/*`] MAY be used to connect otherwise disjoint parts of an authorization network. The motivation is to express the intention of automatically redelegtaing (or "forwarding") authorioty to another agent if you are offline, while retaining the ability to [revoke] that link. The clear use case is linking user devices, but also has applications for PoLA "cold" root/admin keys for servers. + +The `ucan/*` ability MAY be used to substitute into any delegation chain. It "forwards" whatever is later in the chain, in effect swapping out the `iss` field. `ucan/*` MUST NOT change the Ability it redelegates. It MAY be scoped to a particular scheme or attach additional caveats. + +``` js +// Anything +{ + "sub": "did:web:example.com", + "can": "ucan/*", + "iff": [], + // ... +} +``` + +``` mermaid +sequenceDiagram + actor Alice + actor Bob + actor Carol + actor Dan + + autonumber + + Note over Alice, Dan: Delegation Setup + + Bob -->> Carol: Delegate(ucan/*) + Alice -->> Bob: Delegate(crud/create, dns:example.com) + Carol -->> Dan: Delegate(crud/create, dns:example.com) + + Note over Alice, Dan: Invoke + Dan ->> Alice: Invoke(crud/create, dns:example.com, txt="hi", proof: [➋,➊,➌]) + + Note over Alice, Dan: Delegation path in ➍ + Alice -->> Bob: Delegate(crud/create, dns:example.com) + + rect rgb(127, 127, 127) + Bob -->> Carol: Delegate(ucan/*) + end + + Carol -->> Dan: Delegate(crud/create, dns:example.com) +``` + +```js +// Only DNS TXT records +{ + "sub": "did:web:example.com", + "can": "ucan/*", + "iff": [ + { "resource": { "scheme": "dns" } }, + { "rrtype": "TXT" } + ], + // ... +} +``` + ## 3.5 Examples ### 3.5.1 Interacting with an HTTP API From 947d8acb2c93deb7e6c68adadfd39ecbc33a2b6e Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 17 Oct 2023 11:03:44 -0700 Subject: [PATCH 090/127] improve numbering --- README.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index b236d4e0..326a8654 100644 --- a/README.md +++ b/README.md @@ -382,22 +382,23 @@ sequenceDiagram autonumber Note over Alice, Dan: Delegation Setup - - Bob -->> Carol: Delegate(ucan/*) - Alice -->> Bob: Delegate(crud/create, dns:example.com) - Carol -->> Dan: Delegate(crud/create, dns:example.com) + Bob -->> Carol: Delegate(ucan/*) + Alice -->> Bob: Delegate(crud/create, dns:example.com) + Carol -->> Dan: Delegate(crud/create, dns:example.com) Note over Alice, Dan: Invoke - Dan ->> Alice: Invoke(crud/create, dns:example.com, txt="hi", proof: [➋,➊,➌]) + Dan ->> Alice: Invoke(crud/create, dns:example.com, txt="hi", proof: [➋,➊,➌]) Note over Alice, Dan: Delegation path in ➍ - Alice -->> Bob: Delegate(crud/create, dns:example.com) + autonumber 1 - rect rgb(127, 127, 127) - Bob -->> Carol: Delegate(ucan/*) - end - - Carol -->> Dan: Delegate(crud/create, dns:example.com) + Alice -->> Bob: Delegate(crud/create, dns:example.com) + + rect rgb(127, 127, 127) + Bob -->> Carol: Delegate(ucan/*) + end + + Carol -->> Dan: Delegate(crud/create, dns:example.com) ``` ```js From d9a2a6e40a47093150f8d443927310ab43b67945 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 17 Oct 2023 11:04:17 -0700 Subject: [PATCH 091/127] fix numbering --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 326a8654..765829b5 100644 --- a/README.md +++ b/README.md @@ -390,14 +390,15 @@ sequenceDiagram Dan ->> Alice: Invoke(crud/create, dns:example.com, txt="hi", proof: [➋,➊,➌]) Note over Alice, Dan: Delegation path in ➍ - autonumber 1 - + autonumber 2 Alice -->> Bob: Delegate(crud/create, dns:example.com) + autonumber 1 rect rgb(127, 127, 127) Bob -->> Carol: Delegate(ucan/*) end + autonumber 3 Carol -->> Dan: Delegate(crud/create, dns:example.com) ``` From 36ca3e61ca620939611abe17bc17a8926bcc520f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 17 Oct 2023 20:15:32 -0700 Subject: [PATCH 092/127] Spelling --- .github/workflows/words-to-ignore.txt | 1 + README.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index c5473ba0..ae4a0d0b 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -33,6 +33,7 @@ Marsen OAuth OCapN Philipp +PoLA Pre-Draft Proto RPC diff --git a/README.md b/README.md index 765829b5..72021292 100644 --- a/README.md +++ b/README.md @@ -358,9 +358,9 @@ flowchart RL ### 3.4.1 Proof Paths with `ucan/*` -Beyond [attenuation], [`ucan/*`] MAY be used to connect otherwise disjoint parts of an authorization network. The motivation is to express the intention of automatically redelegtaing (or "forwarding") authorioty to another agent if you are offline, while retaining the ability to [revoke] that link. The clear use case is linking user devices, but also has applications for PoLA "cold" root/admin keys for servers. +Beyond [attenuation], [`ucan/*`] MAY be used to connect otherwise disjoint parts of an authorization network. The motivation is to express the intention of automatically re-delegating (or "forwarding") authority to another agent if you are offline, while retaining the ability to [revoke] that link. The clear use case is linking user devices, but also has applications for PoLA "cold" root/admin keys for servers. -The `ucan/*` ability MAY be used to substitute into any delegation chain. It "forwards" whatever is later in the chain, in effect swapping out the `iss` field. `ucan/*` MUST NOT change the Ability it redelegates. It MAY be scoped to a particular scheme or attach additional caveats. +The `ucan/*` ability MAY be used to substitute into any delegation chain. It "forwards" whatever is later in the chain, in effect swapping out the `iss` field. `ucan/*` MUST NOT change the Ability it re-delegates. It MAY be scoped to a particular scheme or attach additional caveats. ``` js // Anything From 30958a4403a39d77507d7fbba63f0696626b5a46 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 19 Oct 2023 18:30:43 -0700 Subject: [PATCH 093/127] Consistent naming --- README.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 72021292..9bd46a75 100644 --- a/README.md +++ b/README.md @@ -211,7 +211,7 @@ A Command is the smallest unit of work that can be Invoked. It is akin to a func | [Subject] | `sub` | `DID` | No | | [Command] | `cmd` | `String` | Yes | | [Arguments] | `arg` | `{String : Any}` | Yes | -| [Nonce] | `nnc` | `String` | Yes | +| [Nonce] | `nnc` | `Bytes` | Yes | The `arg` field MUST be defined by the `cmd` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. @@ -244,8 +244,6 @@ Using the JavaScript analogy from the introduction, an Action is similar to wrap The Command (`cmd`) field MUST contain a concrete, dispatchable message that can be sent to the Executor. The Command MUST define the shape of the data in the [Arguments]. -This field can be thought of as the method, message, or trait being sent to the resource. Note that _unlike_ a UCAN Delegation [Ability], which includes hierarchy, a Command MUST be fully concrete. - ### 3.1.2 Arguments The Arguments (`arg`) field, MAY contain any parameters expected by the Command. The Subject MUST be considered the authority on the shape of this data. This field MUST be representable as a map or keyword list. @@ -285,7 +283,7 @@ As [noted in the introduction][lazy-vs-eager], there is a difference between a r | Field | Type | Required | Description | |-------|----------------------|----------|----------------------------------------------------------| | `uci` | `&InvocationPayload` | Yes | The CID of the [Invocation Payload] | -| `sig` | `&Signature` | Yes | A signature by the Payload's `iss` over the CID in `uci` | +| `sig` | `Signature` | Yes | A signature by the Payload's `iss` over the CID in `uci` | ### 3.3.2 Invocation Payload @@ -360,7 +358,7 @@ flowchart RL Beyond [attenuation], [`ucan/*`] MAY be used to connect otherwise disjoint parts of an authorization network. The motivation is to express the intention of automatically re-delegating (or "forwarding") authority to another agent if you are offline, while retaining the ability to [revoke] that link. The clear use case is linking user devices, but also has applications for PoLA "cold" root/admin keys for servers. -The `ucan/*` ability MAY be used to substitute into any delegation chain. It "forwards" whatever is later in the chain, in effect swapping out the `iss` field. `ucan/*` MUST NOT change the Ability it re-delegates. It MAY be scoped to a particular scheme or attach additional caveats. +The `ucan/*` Command MAY be used to substitute into any delegation chain. It "forwards" whatever is later in the chain, in effect swapping out the `iss` field. `ucan/*` MUST NOT change the Ability it re-delegates. It MAY be scoped to a particular scheme or attach additional caveats. ``` js // Anything @@ -497,7 +495,7 @@ sequenceDiagram "fuel": 999999 }, "act": { - "nnc": "", // NOTE: as stated above, idempotent Actions should always have the same nonce + "nnc": null, // NOTE: as stated above, idempotent Actions should always have the same nonce "cmd": "wasm/run", "arg": { "mod": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", @@ -685,7 +683,6 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen -[Ability]: https://github.com/ucan-wg/delegation#33-abilities [Agoric]: https://agoric.com/ [BCP 14]: https://www.rfc-editor.org/info/bcp14 [Bacalhau]: https://www.bacalhau.org/ @@ -718,7 +715,6 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Rod Vagg]: https://github.com/rvagg/ [Simon Worthington]: https://github.com/simonwo [Spritely Institute]: https://spritely.institute/news/introducing-a-distributed-debugger-for-goblins-with-time-travel.html -[UCAN Ability]: https://github.com/ucan-wg/delegation/#23-ability [UCAN Delegation]: https://github.com/ucan-wg/delegation/ [UCAN Promise]: https://github.com/ucan-wg/promise/ [URI]: https://en.wikipedia.org/wiki/Uniform_Resource_Identifier From 3897976534cfbc9094ef8091ad819d24accf49bc Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 2 Nov 2023 16:58:09 -0700 Subject: [PATCH 094/127] Fix multiple inconsistencies from otehr specs --- README.md | 92 +++++++++++++++++++++++++++---------------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 9bd46a75..260c38a3 100644 --- a/README.md +++ b/README.md @@ -206,14 +206,14 @@ flowchart RL A Command is the smallest unit of work that can be Invoked. It is akin to a function call or actor message. It MUST conform to the following struct shape: -| Name | Field | Type | Required | -|-------------|-------|------------------|----------| -| [Subject] | `sub` | `DID` | No | -| [Command] | `cmd` | `String` | Yes | -| [Arguments] | `arg` | `{String : Any}` | Yes | -| [Nonce] | `nnc` | `Bytes` | Yes | +| Name | Field | Type | Required | +|-------------|---------|------------------|----------| +| [Subject] | `sub` | `DID` | No | +| [Command] | `cmd` | `String` | Yes | +| [Arguments] | `args` | `{String : Any}` | Yes | +| [Nonce] | `nonce` | `Bytes \| null` | Yes | -The `arg` field MUST be defined by the `cmd` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. +The `args` field MUST be defined by the `cmd` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. Using the JavaScript analogy from the introduction, an Action is similar to wrapping a call in a closure: @@ -246,7 +246,7 @@ The Command (`cmd`) field MUST contain a concrete, dispatchable message that can ### 3.1.2 Arguments -The Arguments (`arg`) field, MAY contain any parameters expected by the Command. The Subject MUST be considered the authority on the shape of this data. This field MUST be representable as a map or keyword list. +The Arguments (`args`) field, MAY contain any parameters expected by the Command. The Subject MUST be considered the authority on the shape of this data. This field MUST be representable as a map or keyword list. UCAN capabilities provided in proofs MAY impose certain constraint on the type of Arguments allowed. @@ -259,18 +259,18 @@ The OPTIONAL `sub` field is intended for cases where parameterizing a specific a ### 3.1.4 Nonce -If present, the REQUIRED `nnc` field MUST include a random nonce expressed in ASCII. This field ensures that multiple (non-idempotent) invocations are unique. The nonce SHOULD be `""` for Commands that are idempotent (such as deterministic Wasm modules or standards-abiding HTTP PUT requests). +The REQUIRED `nonce` field MUST include a random nonce. This field ensures that multiple (non-idempotent) invocations are unique. The nonce SHOULD be empty (`0x`) for Commands that are idempotent (such as deterministic Wasm modules or standards-abiding HTTP PUT requests). ## 3.2 Task A Task wraps a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. -| Field | Type | Required | Description | -|-------|----------------------------|----------|--------------------------------------------------------------------------------| -| `act` | `&Command` | Yes | The CID of the [Task] to be run | -| `mta` | `{String : Any}` | Yes | Extensible fields, e.g. resource limits, human-readable tags, notes, and so on | -| `prf` | `[&Delegation]` | Yes | [UCAN Delegation]s that provide the authority to perform the `act` [Action] | -| `exp` | `Integer \| null`[^js-num] | Yes | The UTC Unix timestamp at which the Task expires | +| Field | Type | Required | Description | +|--------|----------------------------|----------|--------------------------------------------------------------------------------| +| `act` | `&Command` | Yes | The CID of the [Task] to be run | +| `meta` | `{String : Any}` | Yes | Extensible fields, e.g. resource limits, human-readable tags, notes, and so on | +| `prf` | `[&Delegation]` | Yes | [UCAN Delegation]s that provide the authority to perform the `act` [Action] | +| `exp` | `Integer \| null`[^js-num] | Yes | The UTC Unix timestamp at which the Task expires | The CID of a Task is useful for reverse look-ups in [Receipt]-sharing networks to check if someone else has run this Task before, and in [UCAN Promise] to connect Tasks together. @@ -289,12 +289,12 @@ As [noted in the introduction][lazy-vs-eager], there is a difference between a r The Invocation Payload attaches sender, receiver, and provenance to the [Task]. -| Field | Type | Required | Description | -|-------|------------|----------|-----------------------------------------------------------| -| `iss` | `DID` | Yes | The DID of the [Invoker] | -| `aud` | `DID` | Yes | The DID of the intended [Executor] | -| `run` | `&Task` | Yes | The enclosed [Task]'s CID | -| `cau` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | +| Field | Type | Required | Description | +|---------|------------|----------|-----------------------------------------------------------| +| `iss` | `DID` | Yes | The DID of the [Invoker] | +| `aud` | `DID` | Yes | The DID of the intended [Executor] | +| `run` | `&Task` | Yes | The enclosed [Task]'s CID | +| `cause` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | ## 3.4 Proof Chains @@ -365,7 +365,7 @@ The `ucan/*` Command MAY be used to substitute into any delegation chain. It "fo { "sub": "did:web:example.com", "can": "ucan/*", - "iff": [], + "if": [], // ... } ``` @@ -401,14 +401,14 @@ sequenceDiagram ``` ```js -// Only DNS TXT records +// Only DNS resources { "sub": "did:web:example.com", "can": "ucan/*", - "iff": [ - { "resource": { "scheme": "dns" } }, - { "rrtype": "TXT" } - ], + "args": { + "scheme": "dns:" + } + "if": [], // ... } ``` @@ -420,14 +420,14 @@ sequenceDiagram ```js { "sig": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, - "inv": { // ┐ + "pld": { // ┐ "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", // │ "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", // │ "run": cid({ // ┐ │ "act": cid({ // ┐ │ │ - "nnc": "&NCC-1701-D*", // │ │ │ + "nonce": {"/": {"bytes": "TWFueSBopvcs"}}, // │ │ │ "cmd": "crud/create", // │ │ │ - "arg": { // │ │ │ + "args": { // │ │ │ "uri": "https://example.com/blog/posts", // │ │ │ "headers": { // │ │ │ "content-type": "application/json" // │ │ │ @@ -440,7 +440,7 @@ sequenceDiagram } // │ │ │ } // │ │ │ }), // ┘ │ │ - "mta": { // │ │ + "meta": { // │ │ "env": "development", // │ │ "tags": ["blog", "post", "pr#123"] // │ │ }, // │ │ @@ -465,9 +465,9 @@ sequenceDiagram "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", "run": cid({ "act": { - "nnc": "email-akiko#1234567890" + "nonce": {"/": {"bytes": "TWFueSBopZ2h0IHdvcs"}} "cmd": "msg/send", - "arg": { + "args": { "from": "mailto:akiko@example.com", "to": [ "boris@example.com", "carol@example.com" ], "subject": "Coffee", @@ -495,9 +495,9 @@ sequenceDiagram "fuel": 999999 }, "act": { - "nnc": null, // NOTE: as stated above, idempotent Actions should always have the same nonce + "nonce": {"/": {"bytes": ""}}, // NOTE: as stated above, idempotent Actions should always have the same nonce "cmd": "wasm/run", - "arg": { + "args": { "mod": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", "fun": "add_one", "params": [42] @@ -527,17 +527,17 @@ Receipts MUST use the same-or-higher version number as the [Invocation] that the Receipt Payloads MUST conform to the following shape: -| Field | Type | Required | Description | -|-------|--------------------|----------|------------------------------------------------------------------------------------| -| `ucr` | `SemVer` | Yes | The version of this spec that the Receipt conforms to | -| `iss` | `DID` | Yes | The DID of the Executor | -| `ran` | `&Invocation` | Yes | A link to the [Invocation] that the Receipt is for | -| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | -| `out` | `Result` | Yes | The value output of the invocation in [Result] format | -| `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | -| `mta` | `{String : Any}` | Yes | Additional data about the receipt | -| `rec` | `&Receipt` | No | Recursive `Receipt`s if the Invocation was proxied to another Executor | -| `iat` | `Integer`[^js-num] | No | The UTC Unix timestamp at which the Receipt was issued | +| Field | Type | Required | Description | +|--------|--------------------|----------|------------------------------------------------------------------------------------| +| `ucr` | `SemVer` | Yes | The version of this spec that the Receipt conforms to | +| `iss` | `DID` | Yes | The DID of the Executor | +| `ran` | `&Invocation` | Yes | A link to the [Invocation] that the Receipt is for | +| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | +| `out` | `Result` | Yes | The value output of the invocation in [Result] format | +| `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | +| `meta` | `{String : Any}` | Yes | Additional data about the receipt | +| `rec` | `&Receipt` | No | Recursive `Receipt`s if the Invocation was proxied to another Executor | +| `iat` | `Integer`[^js-num] | No | The UTC Unix timestamp at which the Receipt was issued | A few of these fields warrant further comment below. From 307a4ba92f24f2c8c61aa9122ae2b10feb3aff34 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 2 Nov 2023 17:13:39 -0700 Subject: [PATCH 095/127] Update README.md Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com> Signed-off-by: Brooklyn Zelenka --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 260c38a3..2023994c 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ However, there is clearly a distinction between passing a function and invoking ## 1.2 Public Resources -A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard required for initiating flow (e.g. sign up). These cases typically involve a reference passed out of band (such as a web link). Due to [designation without authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain, but this SHOULD NOT be the default behavior. +A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard requirement for initiating a flow (e.g. sign up). These cases typically involve a reference passed out of band (such as a web link). Due to [designation without authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain, but this SHOULD NOT be the default behavior. ## 1.3 Promise Pipelining From b09606839e547ee7cf112d1b070c7450119f40be Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 2 Nov 2023 17:13:50 -0700 Subject: [PATCH 096/127] Update README.md Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com> Signed-off-by: Brooklyn Zelenka --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2023994c..0ed37555 100644 --- a/README.md +++ b/README.md @@ -248,7 +248,7 @@ The Command (`cmd`) field MUST contain a concrete, dispatchable message that can The Arguments (`args`) field, MAY contain any parameters expected by the Command. The Subject MUST be considered the authority on the shape of this data. This field MUST be representable as a map or keyword list. -UCAN capabilities provided in proofs MAY impose certain constraint on the type of Arguments allowed. +UCAN capabilities provided in proofs MAY impose constraints on the type of Arguments allowed. ### 3.1.3 Subject From 6a09f3c059357a97df732909f5b97723036c595f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 2 Nov 2023 17:14:02 -0700 Subject: [PATCH 097/127] Update README.md Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com> Signed-off-by: Brooklyn Zelenka --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ed37555..b57e33b6 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ The invoker MUST be the UCAN delegator. Their DID MUST be authenticated in the ` ### 2.1.2 Executor -The executor is directed to perform some task described in the UCAN by the invoker. +The executor is directed to perform some task described in the UCAN invocation by the invoker. ## 2.2 Life Cycle From e9c078634c31d2420613922991c2b608e6e87340 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 14:44:06 +0200 Subject: [PATCH 098/127] Flatten structure, make sig container harder to mess up --- README.md | 301 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 168 insertions(+), 133 deletions(-) diff --git a/README.md b/README.md index b57e33b6..1155bb8e 100644 --- a/README.md +++ b/README.md @@ -202,26 +202,30 @@ flowchart RL # 3 Request -## 3.1 Action +## 3.1 Task -A Command is the smallest unit of work that can be Invoked. It is akin to a function call or actor message. It MUST conform to the following struct shape: +A Task is the smallest unit of work that can be Invoked. It is akin to a function call or actor message. It MUST conform to the following struct shape: -| Name | Field | Type | Required | -|-------------|---------|------------------|----------| -| [Subject] | `sub` | `DID` | No | -| [Command] | `cmd` | `String` | Yes | -| [Arguments] | `args` | `{String : Any}` | Yes | -| [Nonce] | `nonce` | `Bytes \| null` | Yes | +| Name | Field | Type | Required | | +|-------------|---------|--------------------|----------|--------------------------------------------------------------------------------| +| [Subject] | `sub` | `DID` | No | | +| [Command] | `do` | `String` | Yes | | +| [Arguments] | `args` | `{String : Any}` | Yes | | +| [Nonce] | `nonce` | `Bytes \| null` | Yes | | +| [Meta] | `meta` | `{String : Any}` | Yes | Extensible fields, e.g. resource limits, human-readable tags, notes, and so on | +| [Proofs] | `prf` | `[&Delegation]` | Yes | [UCAN Delegation]s that provide the authority to perform the `act` [Action] | +| [Expiry] | `exp` | `Integer`[^js-num] | No | The UTC Unix timestamp at which the Task expires | +| [Issued At] | `iat` | `Integer`[^js-num] | No | The UTC Unix timestamp at which the Invocation was issued | -The `args` field MUST be defined by the `cmd` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. +The shape of the `args` MUST be defined by the `do` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. Using the JavaScript analogy from the introduction, an Action is similar to wrapping a call in a closure: ```js // Command { - "cmd": "msg/send", - "arg": { + "do": "msg/send", + "args": { "from": "mailto:alice@example.com", "to": [ "bob@example.com", "carol@example.com" ], "subject": "hello", @@ -242,17 +246,17 @@ Using the JavaScript analogy from the introduction, an Action is similar to wrap ### 3.1.1 Command -The Command (`cmd`) field MUST contain a concrete, dispatchable message that can be sent to the Executor. The Command MUST define the shape of the data in the [Arguments]. +The REQUIRED Command (`do`) field MUST contain a concrete, dispatchable message that can be sent to the Executor. The Command MUST define the shape of the data in the [Arguments]. ### 3.1.2 Arguments -The Arguments (`args`) field, MAY contain any parameters expected by the Command. The Subject MUST be considered the authority on the shape of this data. This field MUST be representable as a map or keyword list. +The REQUIRED Arguments (`args`) field, MAY contain any parameters expected by the Command. The Subject MUST be considered the authority on the shape of this data. This field MUST be representable as a map or keyword list. UCAN capabilities provided in proofs MAY impose constraints on the type of Arguments allowed. ### 3.1.3 Subject -The OPTIONAL `sub` field is intended for cases where parameterizing a specific agent is important. This is especially critical for two parts of the life cycle: +The REQUIRED `sub` field both parameterizes over a specific agent, and acts as a namespace for how to interpret the [Command]. This is especially critical for two parts of the life cycle: 1. Specifying a particular `sub` (and thus `aud`) when [enqueuing new Tasks][enqueue] in a Receipt 2. Indexing Receipts for reverse lookup and memoization @@ -263,14 +267,11 @@ The REQUIRED `nonce` field MUST include a random nonce. This field ensures that ## 3.2 Task -A Task wraps a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. +A Task extends a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. -| Field | Type | Required | Description | -|--------|----------------------------|----------|--------------------------------------------------------------------------------| -| `act` | `&Command` | Yes | The CID of the [Task] to be run | -| `meta` | `{String : Any}` | Yes | Extensible fields, e.g. resource limits, human-readable tags, notes, and so on | -| `prf` | `[&Delegation]` | Yes | [UCAN Delegation]s that provide the authority to perform the `act` [Action] | -| `exp` | `Integer \| null`[^js-num] | Yes | The UTC Unix timestamp at which the Task expires | +| Field | Type | Required | Description | +|--------|--------------------|----------|--------------------------------------------------------------------------------| +| `act` | `&Action` | Yes | The CID of the [Task] to be run | The CID of a Task is useful for reverse look-ups in [Receipt]-sharing networks to check if someone else has run this Task before, and in [UCAN Promise] to connect Tasks together. @@ -282,19 +283,20 @@ As [noted in the introduction][lazy-vs-eager], there is a difference between a r | Field | Type | Required | Description | |-------|----------------------|----------|----------------------------------------------------------| -| `uci` | `&InvocationPayload` | Yes | The CID of the [Invocation Payload] | -| `sig` | `Signature` | Yes | A signature by the Payload's `iss` over the CID in `uci` | +| `p` | `&InvocationPayload` | Yes | The CID of the [Invocation Payload] | +| `s` | `Signature` | Yes | A signature by the Payload's `iss` over the CID in `uci` | ### 3.3.2 Invocation Payload The Invocation Payload attaches sender, receiver, and provenance to the [Task]. -| Field | Type | Required | Description | -|---------|------------|----------|-----------------------------------------------------------| -| `iss` | `DID` | Yes | The DID of the [Invoker] | -| `aud` | `DID` | Yes | The DID of the intended [Executor] | -| `run` | `&Task` | Yes | The enclosed [Task]'s CID | -| `cause` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | +| Field | Type | Required | Description | +|---------|------------|----------|--------------------------------------------------------------------| +| `iss` | `DID` | Yes | The DID of the [Invoker] | +| `sub` | `DID` | Yes | The [Subject] being invoked | +| `aud` | `DID` | No | The DID of the intended [Executor] if different from the [Subject] | +| `run` | `&Task` | Yes | The enclosed [Task]'s CID | +| `cause` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | ## 3.4 Proof Chains @@ -354,18 +356,18 @@ flowchart RL prf --> Delegations ``` -### 3.4.1 Proof Paths with `ucan/*` +### 3.4.1 Proxied Proof Paths with `ucan/proxy` -Beyond [attenuation], [`ucan/*`] MAY be used to connect otherwise disjoint parts of an authorization network. The motivation is to express the intention of automatically re-delegating (or "forwarding") authority to another agent if you are offline, while retaining the ability to [revoke] that link. The clear use case is linking user devices, but also has applications for PoLA "cold" root/admin keys for servers. +Beyond [attenuation], [`ucan/proxy`] MAY be used to connect otherwise disjoint parts of an authorization network. The motivation is to express the intention of automatically re-delegating (or "forwarding") authority to another agent if you are offline, while retaining the ability to [revoke] that link. The clear use case is linking user devices, but also has applications for PoLA "cold" root/admin keys for servers. -The `ucan/*` Command MAY be used to substitute into any delegation chain. It "forwards" whatever is later in the chain, in effect swapping out the `iss` field. `ucan/*` MUST NOT change the Ability it re-delegates. It MAY be scoped to a particular scheme or attach additional caveats. +The `ucan/proxy` Command MAY be used to substitute into any delegation chain. It "forwards" whatever is later in the chain, in effect swapping out the `iss` field. `ucan/proxy` MUST NOT change the Ability it re-delegates. It MAY be scoped to a particular scheme or attach additional caveats. ``` js // Anything { "sub": "did:web:example.com", - "can": "ucan/*", - "if": [], + "can": "ucan/proxy", + "cond": [], // ... } ``` @@ -380,7 +382,7 @@ sequenceDiagram autonumber Note over Alice, Dan: Delegation Setup - Bob -->> Carol: Delegate(ucan/*) + Bob -->> Carol: Delegate(ucan/proxy) Alice -->> Bob: Delegate(crud/create, dns:example.com) Carol -->> Dan: Delegate(crud/create, dns:example.com) @@ -393,7 +395,7 @@ sequenceDiagram autonumber 1 rect rgb(127, 127, 127) - Bob -->> Carol: Delegate(ucan/*) + Bob -->> Carol: Delegate(ucan/proxy) end autonumber 3 @@ -404,9 +406,9 @@ sequenceDiagram // Only DNS resources { "sub": "did:web:example.com", - "can": "ucan/*", + "can": "ucan/proxy", "args": { - "scheme": "dns:" + "scheme": "dns" } "if": [], // ... @@ -418,67 +420,70 @@ sequenceDiagram ### 3.5.1 Interacting with an HTTP API ```js +// DAG-JSON { - "sig": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, - "pld": { // ┐ - "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", // │ - "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", // │ - "run": cid({ // ┐ │ - "act": cid({ // ┐ │ │ - "nonce": {"/": {"bytes": "TWFueSBopvcs"}}, // │ │ │ - "cmd": "crud/create", // │ │ │ - "args": { // │ │ │ - "uri": "https://example.com/blog/posts", // │ │ │ - "headers": { // │ │ │ - "content-type": "application/json" // │ │ │ - }, // ├── Action │ │ - "payload": { // │ │ │ - "title": "UCAN for Fun an Profit", // │ │ │ - "body": "UCAN is great!", // │ ├── Task ├── Payload - "topics": ["authz", "journal"], // │ │ │ - "draft": true // │ │ │ - } // │ │ │ - } // │ │ │ - }), // ┘ │ │ - "meta": { // │ │ - "env": "development", // │ │ - "tags": ["blog", "post", "pr#123"] // │ │ - }, // │ │ - "prf": [ // │ │ - {"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}, // │ │ - {"/": "bafkr4idnrqfouibxdqpvh2lmkhgsbw5yabvjbiaea3fplrb4vxifaphvgy"}, // │ │ - {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"} // │ │ - ], // │ │ - "exp": 1697409438 // │ │ - }) // ┘ │ - } // ┘ + "s": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, + "p": { + { + "h": {"/": {"bytes": "NBIFEgEAcQ"}}, + "ucan/i/1.0.0-rc.1": { + "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", + "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", + "do": "crud/create", + "args": { + "uri": "https://example.com/blog/posts", + "headers": { + "content-type": "application/json" + }, + "payload": { + "title": "UCAN for Fun an Profit", + "body": "UCAN is great!", + "topics": ["authz", "journal"], + "draft": true + } + }, + "nonce": {"/": {"bytes": "TWFueSBopvcs"}}, + "meta": { + "env": "development", + "tags": ["blog", "post", "pr#123"] + }, + "exp": 1697409438 + "prf": [ + {"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}, + {"/": "bafkr4idnrqfouibxdqpvh2lmkhgsbw5yabvjbiaea3fplrb4vxifaphvgy"}, + {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"} + ] + } + } + } } ``` ### 3.5.2 Sending Email ```js +// DAG-JSON { - "sig": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, - "inv": cid({ - "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", - "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", - "run": cid({ - "act": { - "nonce": {"/": {"bytes": "TWFueSBopZ2h0IHdvcs"}} - "cmd": "msg/send", - "args": { - "from": "mailto:akiko@example.com", - "to": [ "boris@example.com", "carol@example.com" ], - "subject": "Coffee", - "body": "Let get coffee sometime and talk about UCAN Invocations!" - } - }), + "s": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, + "p": { + "h": {"/": {"bytes": "NBIFEgEAcQ"}}, + "ucan/i/1.0.0-rc.1": { + "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", + "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", + "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", + "do": "msg/send", + "args": { + "from": "mailto:akiko@example.com", + "to": [ "boris@example.com", "carol@example.com" ], + "subject": "Coffee", + "body": "Let get coffee sometime and talk about UCAN Invocations!" + }, + "nonce": {"/": {"bytes": "TWFueSBopZ2h0IHdvcs"}}, "mta": {}, "prf": [{"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}], "exp": 1697409438 } - }) + } } ``` @@ -486,42 +491,45 @@ sequenceDiagram ```js { - "sig": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, - "inv": cid({ - "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", - "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", - "run": cid({ - "mta": { - "fuel": 999999 - }, - "act": { - "nonce": {"/": {"bytes": ""}}, // NOTE: as stated above, idempotent Actions should always have the same nonce - "cmd": "wasm/run", - "args": { - "mod": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", - "fun": "add_one", - "params": [42] - } + "s": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, + "p": { + "h": {"/": {"bytes": "NBIFEgEAcQ"}}, + "ucan/i/1.0.0-rc.1": { + "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", + "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", + "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", + "meta": {"fuel": 999999}, + "nonce": {"/": {"bytes": ""}}, // NOTE: as stated above, idempotent Actions should always have the same nonce + "do": "wasm/run", + "args": { + "mod": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", + "fun": "add_one", + "params": [42] } - }) - }) + } + } } ``` # 4 Response -## 4.1 Receipt (Envelope) - -A `Receipt` is an attestation of the [Receipt Payload]. A Receipt MUST be signed by the [Executor] (including by [Execution Proxy]). +A `Receipt` is a kind of Invocation used to attest to the result of another Invocation. A Receipt MUST be issued by the [Executor] (including by [Execution Proxy]). **NB: a Receipt does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. Receipts MUST use the same-or-higher version number as the [Invocation] that they reference. -| Field | Type | Required | Description | -|-------|-------------------|----------|--------------------------------------------------------------------------| -| `ucr` | `&ReceiptPayload` | Yes | The CID for the [Receipt Payload] | -| `sig` | `Signature` | Yes | The [Signature] of the `uci` value, by the [Receipt Payload]'s `iss` DID | +FIXME + +| Field | Type | Required | Description | +|-------|-------------|----------|------------------------------------------------------------------------------------------------| +| `s` | `Signature` | Yes | Signature (bytes or struct) of the `pld` field, which MUST be interpreted as the `pld.h` field | +| `p` | `&Payload` | Yes | The data being signed over | + +| Field | Type | Required | Description | +|---------------------|------------------|----------|------------------------------------------------------| +| `h` | `VarsigHeader` | Yes | [Varsig] header that describes the outer `sig` field | +| `ucan/r/1.0.0-rc.1` | `ReceiptPayload` | Yes | Fields unique to the Receipt | ## 4.2 Receipt Payload @@ -529,19 +537,17 @@ Receipt Payloads MUST conform to the following shape: | Field | Type | Required | Description | |--------|--------------------|----------|------------------------------------------------------------------------------------| -| `ucr` | `SemVer` | Yes | The version of this spec that the Receipt conforms to | | `iss` | `DID` | Yes | The DID of the Executor | | `ran` | `&Invocation` | Yes | A link to the [Invocation] that the Receipt is for | -| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | | `out` | `Result` | Yes | The value output of the invocation in [Result] format | -| `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | +| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | +| `req` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | | `meta` | `{String : Any}` | Yes | Additional data about the receipt | -| `rec` | `&Receipt` | No | Recursive `Receipt`s if the Invocation was proxied to another Executor | | `iat` | `Integer`[^js-num] | No | The UTC Unix timestamp at which the Receipt was issued | A few of these fields warrant further comment below. -### 4.2.1 Result +### 4.1 Result A Result records the output of the [Task], as well as its success or failure state. A Result MUST be formatted as map with a single `tag` field. @@ -563,7 +569,7 @@ A Result records the output of the [Task], as well as its success or failure sta } ``` -### 4.2.2 Enqueue +## 4.2 Enqueue Request The result of an [Invocation] MAY include a request for further actions to be performed. This is a process of requesting that the invoker "enqueue" a new Task. This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. @@ -571,7 +577,27 @@ Enqueued [Task]s describe requests for future work to be performed. They SHOULD All [Task]s in an [enqueue] array MUST be treated as concurrent, unless explicit data dependencies between them exist via [UCAN Promise]s. -### 4.2.3 Proxy Execution +## 4.3 Examples + +``` js +// DAG-JSON +{ + "s": {"/": {"bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU"}}, + "p": { + "h": {"/": {"bytes": "NBIFEgEAcQ"}}, + "ucan/r/1.0.0-rc.1": { + "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", + "ran": {"/": FIXME}, + "out": {"ok": 42}, + "iat": 1702907627, + "meta": {}, + "prf": [], // FIXME document & diagram that this only has to complete the chain + } + } +} +``` + +# 5 Proxy Execution If the Receipt Issuer is not identical to the `aud` field of Invocation referenced in the `ran` field, a [Delegation] proof chain SHOULD be included. If a chain is present, it MUST show that such a proxy execution was authorized by the original listed `aud` Agent. @@ -608,25 +634,34 @@ const receipt = { } ``` -## 4.3 Examples +## 5.1 Examples -``` json +``` js +// DAG-JSON { - "ucr": { - "ran": {"/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu"}, - "out": { - "ok": ["bob@example.com", "alice@example.com"] - }, - "mta": { - "retry-count": 2, - "total-time": [400, "hours"] + "s": {"/": {"bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU"}}, + "p": { + "h": {"/": {"bytes": "NBIFEgEAcQ"}}, + "ucan/i/1.0.0-rc.1": { + "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", + "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", + "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", + "do": "ucan/receipt", + "args": { + "ran": {"/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu"}, + "out": {"ok": ["bob@example.com", "alice@example.com"]} + }, + "meta": { + "retry-count": 2, + "total-time": [400, "hours"] + }, + "prf": [] } - }, - "sig": {"/": {"bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU"}} + } } ``` -# 5 Prior Art +# 6 Prior Art [ucanto RPC] from [DAG House] is a production system that uses UCAN as the basis for an RPC layer. @@ -638,7 +673,7 @@ The Object Capability Network ([OCapN]) protocol extends [CapTP] with a generali [Cap 'n Proto RPC] is an influential RPC framework based on concepts from [CapTP]. -# 6 Acknowledgements +# 7 Acknowledgements Many thanks to [Mark Miller] for his [trail blazing work][eRights] on [capability systems]. From c97a22e5fb593802adf3dee6105a6c065d1e52ae Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 14:56:08 +0200 Subject: [PATCH 099/127] Fixup table --- README.md | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 1155bb8e..0767d938 100644 --- a/README.md +++ b/README.md @@ -133,8 +133,8 @@ The executor is directed to perform some task described in the UCAN invocation b At a very high level: -- A [Task] abstractly requests some action -- An [Invocation] attaches proven ([delegated][Delegation]) authority to a [Task] +- A [Task] abstractly describes some Action to be run +- An [Invocation] attaches proven ([delegated][Delegation]) authority to a [Task], and requests it be run by a certain Agent - A [Receipt] MAY request that the Invoker enqueue more [Task]s ``` mermaid @@ -206,16 +206,16 @@ flowchart RL A Task is the smallest unit of work that can be Invoked. It is akin to a function call or actor message. It MUST conform to the following struct shape: -| Name | Field | Type | Required | | -|-------------|---------|--------------------|----------|--------------------------------------------------------------------------------| -| [Subject] | `sub` | `DID` | No | | -| [Command] | `do` | `String` | Yes | | -| [Arguments] | `args` | `{String : Any}` | Yes | | -| [Nonce] | `nonce` | `Bytes \| null` | Yes | | -| [Meta] | `meta` | `{String : Any}` | Yes | Extensible fields, e.g. resource limits, human-readable tags, notes, and so on | -| [Proofs] | `prf` | `[&Delegation]` | Yes | [UCAN Delegation]s that provide the authority to perform the `act` [Action] | -| [Expiry] | `exp` | `Integer`[^js-num] | No | The UTC Unix timestamp at which the Task expires | -| [Issued At] | `iat` | `Integer`[^js-num] | No | The UTC Unix timestamp at which the Invocation was issued | +| Name | Field | Type | Required | +|-------------|---------|--------------------|----------| +| [Subject] | `sub` | `DID` | No | +| [Command] | `do` | `String` | Yes | +| [Arguments] | `args` | `{String : Any}` | Yes | +| [Nonce] | `nonce` | `Bytes \| null` | Yes | +| [Meta] | `meta` | `{String : Any}` | Yes | +| [Proofs] | `prf` | `[&Delegation]` | Yes | +| [Expiry] | `exp` | `Integer`[^js-num] | No | +| [Issued At] | `iat` | `Integer`[^js-num] | No | The shape of the `args` MUST be defined by the `do` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. @@ -265,6 +265,14 @@ The REQUIRED `sub` field both parameterizes over a specific agent, and acts as a The REQUIRED `nonce` field MUST include a random nonce. This field ensures that multiple (non-idempotent) invocations are unique. The nonce SHOULD be empty (`0x`) for Commands that are idempotent (such as deterministic Wasm modules or standards-abiding HTTP PUT requests). +### 3.1.5 Metadata + +### 3.1.6 Proofs + +### 3.1.7 Expiry + +### 3.1.9 Issuance Timestamp + ## 3.2 Task A Task extends a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. From d27744fe288ca62255c57f503f1b120f1c01084c Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 15:14:06 +0200 Subject: [PATCH 100/127] Clarify cryptography --- README.md | 60 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 0767d938..e5fe1a2d 100644 --- a/README.md +++ b/README.md @@ -212,7 +212,7 @@ A Task is the smallest unit of work that can be Invoked. It is akin to a functio | [Command] | `do` | `String` | Yes | | [Arguments] | `args` | `{String : Any}` | Yes | | [Nonce] | `nonce` | `Bytes \| null` | Yes | -| [Meta] | `meta` | `{String : Any}` | Yes | +| [Metadata] | `meta` | `{String : Any}` | Yes | | [Proofs] | `prf` | `[&Delegation]` | Yes | | [Expiry] | `exp` | `Integer`[^js-num] | No | | [Issued At] | `iat` | `Integer`[^js-num] | No | @@ -267,34 +267,64 @@ The REQUIRED `nonce` field MUST include a random nonce. This field ensures that ### 3.1.5 Metadata +The `meta` field MAY include arbitrary metadata or extensable fields. For example, Wasm fuel, an internal job ID, references to GitHub Issues, and so on. This data MAY be used by the Executor. + ### 3.1.6 Proofs +The `prf` field defines all [Delegation]s required to prove that this Invocation has an unbroken authorization chain. + ### 3.1.7 Expiry -### 3.1.9 Issuance Timestamp +The OPTIONAL field `exp` defines when the Invocation SHOULD time out. This is both expressive (defines a timeout, which is a best practice), and prevents replays. -## 3.2 Task +### 3.1.8 Issued At -A Task extends a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. +The `iat` field MAY contain an issuance timestamp. This time SHOULD NOT be trusted; it is only a claim by the Invoker of their system time. System clocks often have clock skew, or a Byzantine Invoker could claim an arbitrary time. -| Field | Type | Required | Description | -|--------|--------------------|----------|--------------------------------------------------------------------------------| -| `act` | `&Action` | Yes | The CID of the [Task] to be run | +## 3.2 Task -The CID of a Task is useful for reverse look-ups in [Receipt]-sharing networks to check if someone else has run this Task before, and in [UCAN Promise] to connect Tasks together. +A Task extends a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. Indexing the CID of a Task is useful for reverse look-ups in [Receipt]-sharing networks to check if someone else has run this Task before, and in [UCAN Promise] to connect Tasks together. ## 3.3 Invocation As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is a request to the [Executor] to perform the enclosed [Task]. [Invocation Payload]s are not executable until they have been signed and [Delegation] proofs validated. +``` mermaid +flowchart TD + subgraph Invocation + SignatureBytes["Signature (raw bytes)"] + + subgraph SigPayload ["Signature Payload"] + VarsigHeader["Varsig Header"] + + subgraph InvocationPayload + direction + + iss + sub + do + args + etc["..."] + end + end + end +``` + ### 3.3.1 Invocation (Envelope) -| Field | Type | Required | Description | -|-------|----------------------|----------|----------------------------------------------------------| -| `p` | `&InvocationPayload` | Yes | The CID of the [Invocation Payload] | -| `s` | `Signature` | Yes | A signature by the Payload's `iss` over the CID in `uci` | +| Field | Type | Required | Description | +|-------|--------------------|----------|-------------------------------------------------------| +| `s` | `Signature` | Yes | A signature by the Payload's `iss` over the `p` field | +| `p` | `SignaturePayload` | Yes | The Siganture Payload | + +### 3.3.2 Signature Payload + +| Field | Type | Required | Description | +|---------------------|----------------------|----------|---------------------------------------------------------------------------------------------------------------| +| `h` | `VarsigHeader` | Yes | The Varsig header, describing the cryptographic configuration used to format and sign the [Invocaion Payload] | +| `ucan/i/1.0.0-rc.1` | `Invocation Payload` | Yes | The [Invocation Payload] | -### 3.3.2 Invocation Payload +### 3.3.3 Invocation Payload The Invocation Payload attaches sender, receiver, and provenance to the [Task]. @@ -710,11 +740,15 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Command]: #311-command [Execution Proxy]: #423-proxy-execution [Executor]: #212-executor +[Expiry]: #317-expiry [Invocation Payload]: #331-invocation-payload [Invocation Signature]: #331-invocation-envelope [Invocation]: #33-invocation [Invoker]: #211-invoker +[Issued At]: #318-issued-at +[Metadata]: #315-metadata [Nonce]: #314-nonce +[Proofs]: #316-proofs [Receipt Payload]: #42-receipt-payload [Receipt]: #41-receipt-envelope [Response]: #4-response From 37afdc1b7c08292227aacedf59bfaa9fe8fdc8d6 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 15:56:30 +0200 Subject: [PATCH 101/127] Remove section about Tasks in particular --- README.md | 188 ++++++++++++++++++++++++++---------------------------- 1 file changed, 92 insertions(+), 96 deletions(-) diff --git a/README.md b/README.md index e5fe1a2d..69080df2 100644 --- a/README.md +++ b/README.md @@ -202,24 +202,72 @@ flowchart RL # 3 Request -## 3.1 Task +A request for some work to be done (or to "exercise your authority") is an Invocation. -A Task is the smallest unit of work that can be Invoked. It is akin to a function call or actor message. It MUST conform to the following struct shape: +``` mermaid +flowchart TD + subgraph Invocation + SignatureBytes["Signature (raw bytes)"] + + subgraph SigPayload ["Signature Payload"] + VarsigHeader["Varsig Header"] -| Name | Field | Type | Required | -|-------------|---------|--------------------|----------| -| [Subject] | `sub` | `DID` | No | -| [Command] | `do` | `String` | Yes | -| [Arguments] | `args` | `{String : Any}` | Yes | -| [Nonce] | `nonce` | `Bytes \| null` | Yes | -| [Metadata] | `meta` | `{String : Any}` | Yes | -| [Proofs] | `prf` | `[&Delegation]` | Yes | -| [Expiry] | `exp` | `Integer`[^js-num] | No | -| [Issued At] | `iat` | `Integer`[^js-num] | No | + subgraph InvocationPayload ["Invocation Payload"] + direction + + iss + sub + do + args + prf + etc["..."] + end + end + end +``` -The shape of the `args` MUST be defined by the `do` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. +As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is a request to the [Executor] to perform the enclosed [Task]. [Invocation Payload]s are not executable until they have been signed and [Delegation] proofs validated. -Using the JavaScript analogy from the introduction, an Action is similar to wrapping a call in a closure: +Note that the Invocation MUST include the Signature envelope. An [Invocation Payload] on its own MUST NOT be considered a valid Invocation. + +## 3.1 Invocation (Envelope) + +| Field | Type | Required | Description | +|-------|--------------------|----------|-------------------------------------------------------| +| `s` | `Signature` | Yes | A signature by the Payload's `iss` over the `p` field | +| `p` | `SignaturePayload` | Yes | The content that was signed | + +## 3.2 Signature Payload + +| Field | Type | Required | Description | +|---------------------|---------------------|----------|---------------------------------------------------------------------------------------------------------------| +| `h` | `VarsigHeader` | Yes | The Varsig header, describing the cryptographic configuration used to format and sign the [Invocaion Payload] | +| `ucan/i/1.0.0-rc.1` | `InvocationPayload` | Yes | The [Invocation Payload] | + +The Signature Payload MUST contain a [Varsig] header, and the [Invocation Payload]. The Varsig header MUST be inside the signature payload as it: + +1. Commits the Signature to the cryptographic algorithms used +2. Describes how the paylaod was serialized before signing + +## 3.3 Invocation Payload + +The Invocation Payload attaches sender, receiver, and provenance to the [Task]. + +| Field | Type | Required | Description | | +|-------------|---------|--------------------|-------------|--------------------------------------------------------------------| +| [Issuer] | `iss` | `DID` | Yes | The DID of the [Invoker] | +| [Subject] | `sub` | `DID` | Yes | The [Subject] being invoked | +| [Audience] | `aud` | `DID` | No | The DID of the intended [Executor] if different from the [Subject] | +| [Command] | `do` | `String` | Yes | | +| [Arguments] | `args` | `{String : Any}` | Yes | | +| [Nonce] | `nonce` | `Bytes \| null` | Yes | | +| [Metadata] | `meta` | `{String : Any}` | Yes | | +| [Proofs] | `prf` | `[&Delegation]` | Yes | | +| [Expiry] | `exp` | `Integer`[^js-num] | No | | +| [Issued At] | `iat` | `Integer`[^js-num] | No | | +| [Cause] | `cause` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | + +The shape of the `args` MUST be defined by the `do` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. Using the JavaScript analogy from the introduction, an Action is similar to wrapping a call in a closure: ```js // Command @@ -281,60 +329,9 @@ The OPTIONAL field `exp` defines when the Invocation SHOULD time out. This is bo The `iat` field MAY contain an issuance timestamp. This time SHOULD NOT be trusted; it is only a claim by the Invoker of their system time. System clocks often have clock skew, or a Byzantine Invoker could claim an arbitrary time. -## 3.2 Task - -A Task extends a [Command] with contextual information. This includes expiration time, delegation chain, and extensible metadata for things like resource limits. Indexing the CID of a Task is useful for reverse look-ups in [Receipt]-sharing networks to check if someone else has run this Task before, and in [UCAN Promise] to connect Tasks together. - -## 3.3 Invocation - -As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is a request to the [Executor] to perform the enclosed [Task]. [Invocation Payload]s are not executable until they have been signed and [Delegation] proofs validated. - -``` mermaid -flowchart TD - subgraph Invocation - SignatureBytes["Signature (raw bytes)"] - - subgraph SigPayload ["Signature Payload"] - VarsigHeader["Varsig Header"] - - subgraph InvocationPayload - direction +## 3.2 Attestation - iss - sub - do - args - etc["..."] - end - end - end -``` - -### 3.3.1 Invocation (Envelope) - -| Field | Type | Required | Description | -|-------|--------------------|----------|-------------------------------------------------------| -| `s` | `Signature` | Yes | A signature by the Payload's `iss` over the `p` field | -| `p` | `SignaturePayload` | Yes | The Siganture Payload | - -### 3.3.2 Signature Payload - -| Field | Type | Required | Description | -|---------------------|----------------------|----------|---------------------------------------------------------------------------------------------------------------| -| `h` | `VarsigHeader` | Yes | The Varsig header, describing the cryptographic configuration used to format and sign the [Invocaion Payload] | -| `ucan/i/1.0.0-rc.1` | `Invocation Payload` | Yes | The [Invocation Payload] | - -### 3.3.3 Invocation Payload - -The Invocation Payload attaches sender, receiver, and provenance to the [Task]. - -| Field | Type | Required | Description | -|---------|------------|----------|--------------------------------------------------------------------| -| `iss` | `DID` | Yes | The DID of the [Invoker] | -| `sub` | `DID` | Yes | The [Subject] being invoked | -| `aud` | `DID` | No | The DID of the intended [Executor] if different from the [Subject] | -| `run` | `&Task` | Yes | The enclosed [Task]'s CID | -| `cause` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | +An Invocation MAY be used to attest to some information. This is in effect a statement to the Issuer (without Audience) that never expires. ## 3.4 Proof Chains @@ -462,37 +459,36 @@ sequenceDiagram { "s": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, "p": { - { - "h": {"/": {"bytes": "NBIFEgEAcQ"}}, - "ucan/i/1.0.0-rc.1": { - "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", - "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", - "do": "crud/create", - "args": { - "uri": "https://example.com/blog/posts", - "headers": { - "content-type": "application/json" - }, - "payload": { - "title": "UCAN for Fun an Profit", - "body": "UCAN is great!", - "topics": ["authz", "journal"], - "draft": true - } - }, - "nonce": {"/": {"bytes": "TWFueSBopvcs"}}, - "meta": { - "env": "development", - "tags": ["blog", "post", "pr#123"] + "h": {"/": {"bytes": "NBIFEgEAcQ"}}, + "ucan/i/1.0.0-rc.1": { + "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", + "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", + "do": "crud/create", + "args": { + "uri": "https://example.com/blog/posts", + "headers": { + "content-type": "application/json" }, - "exp": 1697409438 - "prf": [ - {"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}, - {"/": "bafkr4idnrqfouibxdqpvh2lmkhgsbw5yabvjbiaea3fplrb4vxifaphvgy"}, - {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"} - ] - } + "payload": { + "title": "UCAN for Fun an Profit", + "body": "UCAN is great!", + "topics": ["authz", "journal"], + "draft": true + } + }, + "nonce": {"/": {"bytes": "TWFueSBopvcs"}}, + "meta": { + "env": "development", + "tags": ["blog", "post", "pr#123"] + }, + "exp": 1697409438 + "prf": [ + {"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}, + {"/": "bafkr4idnrqfouibxdqpvh2lmkhgsbw5yabvjbiaea3fplrb4vxifaphvgy"}, + {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"} + ] } + } } ``` From 1726f67f05ff3a4bf994ed05c409e7ae33e03a5c Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 16:57:56 +0200 Subject: [PATCH 102/127] That may be it for major changes --- README.md | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 69080df2..72e62dc4 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,7 @@ flowchart TD VarsigHeader["Varsig Header"] subgraph InvocationPayload ["Invocation Payload"] - direction + direction TD iss sub @@ -239,12 +239,12 @@ Note that the Invocation MUST include the Signature envelope. An [Invocation Pay ## 3.2 Signature Payload -| Field | Type | Required | Description | -|---------------------|---------------------|----------|---------------------------------------------------------------------------------------------------------------| -| `h` | `VarsigHeader` | Yes | The Varsig header, describing the cryptographic configuration used to format and sign the [Invocaion Payload] | -| `ucan/i/1.0.0-rc.1` | `InvocationPayload` | Yes | The [Invocation Payload] | +| Field | Type | Required | Description | +|---------------------|---------------------|----------|--------------------------| +| `h` | `VarsigHeader` | Yes | The Varsig header | +| `ucan/i/1.0.0-rc.1` | `InvocationPayload` | Yes | The [Invocation Payload] | -The Signature Payload MUST contain a [Varsig] header, and the [Invocation Payload]. The Varsig header MUST be inside the signature payload as it: +The Signature Payload MUST contain a [Varsig] header, and the [Invocation Payload]. The Varsig header MUST describe the cryptographic configuration used to format and sign the [Invocaion Payload]. This is important in order to: 1. Commits the Signature to the cryptographic algorithms used 2. Describes how the paylaod was serialized before signing @@ -258,13 +258,13 @@ The Invocation Payload attaches sender, receiver, and provenance to the [Task]. | [Issuer] | `iss` | `DID` | Yes | The DID of the [Invoker] | | [Subject] | `sub` | `DID` | Yes | The [Subject] being invoked | | [Audience] | `aud` | `DID` | No | The DID of the intended [Executor] if different from the [Subject] | -| [Command] | `do` | `String` | Yes | | -| [Arguments] | `args` | `{String : Any}` | Yes | | -| [Nonce] | `nonce` | `Bytes \| null` | Yes | | -| [Metadata] | `meta` | `{String : Any}` | Yes | | -| [Proofs] | `prf` | `[&Delegation]` | Yes | | -| [Expiry] | `exp` | `Integer`[^js-num] | No | | -| [Issued At] | `iat` | `Integer`[^js-num] | No | | +| [Command] | `do` | `String` | Yes | The [Command] | +| [Arguments] | `args` | `{String : Any}` | Yes | The [Command]'s [Arguments] | +| [Proofs] | `prf` | `[&Delegation]` | Yes | [Delegation]s that prove the chain of authority | +| [Metadata] | `meta` | `{String : Any}` | No | Arbitrary [Metadata] | +| [Nonce] | `nonce` | `Bytes` | No | A unique, random nonce | +| [Expiry] | `exp` | `Integer`[^js-num] | No | The timestamp at which the Invocation becomes invalid | +| [Issued At] | `iat` | `Integer`[^js-num] | No | The timestamp at which the Invocation was created | | [Cause] | `cause` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | The shape of the `args` MUST be defined by the `do` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. Using the JavaScript analogy from the introduction, an Action is similar to wrapping a call in a closure: @@ -553,13 +553,13 @@ A `Receipt` is a kind of Invocation used to attest to the result of another Invo Receipts MUST use the same-or-higher version number as the [Invocation] that they reference. -FIXME - | Field | Type | Required | Description | |-------|-------------|----------|------------------------------------------------------------------------------------------------| | `s` | `Signature` | Yes | Signature (bytes or struct) of the `pld` field, which MUST be interpreted as the `pld.h` field | | `p` | `&Payload` | Yes | The data being signed over | +## 3.2 Signature Payload + | Field | Type | Required | Description | |---------------------|------------------|----------|------------------------------------------------------| | `h` | `VarsigHeader` | Yes | [Varsig] header that describes the outer `sig` field | @@ -575,7 +575,7 @@ Receipt Payloads MUST conform to the following shape: | `ran` | `&Invocation` | Yes | A link to the [Invocation] that the Receipt is for | | `out` | `Result` | Yes | The value output of the invocation in [Result] format | | `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | -| `req` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | +| `next` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | | `meta` | `{String : Any}` | Yes | Additional data about the receipt | | `iat` | `Integer`[^js-num] | No | The UTC Unix timestamp at which the Receipt was issued | @@ -603,7 +603,7 @@ A Result records the output of the [Task], as well as its success or failure sta } ``` -## 4.2 Enqueue Request +## 4.2 Next Task(s) The result of an [Invocation] MAY include a request for further actions to be performed. This is a process of requesting that the invoker "enqueue" a new Task. This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. @@ -621,11 +621,10 @@ All [Task]s in an [enqueue] array MUST be treated as concurrent, unless explicit "h": {"/": {"bytes": "NBIFEgEAcQ"}}, "ucan/r/1.0.0-rc.1": { "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", - "ran": {"/": FIXME}, + "ran": {"/": "bafkreictzcfwelyww7zmjkl5nptyot24oilky2bppw42nui2acozhfmzqa"}, "out": {"ok": 42}, "iat": 1702907627, - "meta": {}, - "prf": [], // FIXME document & diagram that this only has to complete the chain + "prf": [], } } } From 64172610ac22e30f7a050c209df1efc0c005dd20 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 17:14:24 +0200 Subject: [PATCH 103/127] Task --- README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 72e62dc4..1d083890 100644 --- a/README.md +++ b/README.md @@ -213,8 +213,6 @@ flowchart TD VarsigHeader["Varsig Header"] subgraph InvocationPayload ["Invocation Payload"] - direction TD - iss sub do @@ -329,7 +327,20 @@ The OPTIONAL field `exp` defines when the Invocation SHOULD time out. This is bo The `iat` field MAY contain an issuance timestamp. This time SHOULD NOT be trusted; it is only a claim by the Invoker of their system time. System clocks often have clock skew, or a Byzantine Invoker could claim an arbitrary time. -## 3.2 Attestation +## 3.2 Task + +A Task is the subset of fields that uniquely determine the work to be performed[^subtype]. A Task MUST be unqiuely defined by the following fields: + +- [Subject] +- [Command] +- [Arguments] +- [Nonce] + +The nonce is important for distinguishing between non-idempotent executions of a Task by making the group together unique. + +[^subtype]: An Invocation is thus a subtype of Task: $\textsf{Invocation} <: \textsf{Task}$ + +## 3.3 Attestation An Invocation MAY be used to attest to some information. This is in effect a statement to the Issuer (without Audience) that never expires. From 275a7541e6ac90cbe8d6695e034a6b24048f35d0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 17:18:14 +0200 Subject: [PATCH 104/127] Fix typos --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1d083890..1cb54e46 100644 --- a/README.md +++ b/README.md @@ -329,7 +329,9 @@ The `iat` field MAY contain an issuance timestamp. This time SHOULD NOT be trust ## 3.2 Task -A Task is the subset of fields that uniquely determine the work to be performed[^subtype]. A Task MUST be unqiuely defined by the following fields: +A Task is the subset of Invocation fields that uniquely determine the work to be performed[^subtype]. A Task MUST be unqiuely defined by the following fields: + +[^subtype]: Which is to say: an Invocation is a subtype of Task - [Subject] - [Command] @@ -338,8 +340,6 @@ A Task is the subset of fields that uniquely determine the work to be performed[ The nonce is important for distinguishing between non-idempotent executions of a Task by making the group together unique. -[^subtype]: An Invocation is thus a subtype of Task: $\textsf{Invocation} <: \textsf{Task}$ - ## 3.3 Attestation An Invocation MAY be used to attest to some information. This is in effect a statement to the Issuer (without Audience) that never expires. From 25ed0b0abcd9b219a096a5b2c5a385ae1012996a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 17:26:22 +0200 Subject: [PATCH 105/127] Add broken links --- README.md | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1cb54e46..06ebffd2 100644 --- a/README.md +++ b/README.md @@ -290,43 +290,55 @@ The shape of the `args` MUST be defined by the `do` field type. This is similar }) ``` -### 3.1.1 Command +### 3.1.1 Issuer -The REQUIRED Command (`do`) field MUST contain a concrete, dispatchable message that can be sent to the Executor. The Command MUST define the shape of the data in the [Arguments]. - -### 3.1.2 Arguments - -The REQUIRED Arguments (`args`) field, MAY contain any parameters expected by the Command. The Subject MUST be considered the authority on the shape of this data. This field MUST be representable as a map or keyword list. - -UCAN capabilities provided in proofs MAY impose constraints on the type of Arguments allowed. +The `iss` field MUST include the Issuer of the Invocation. This DID MUST match against the encoding signature. -### 3.1.3 Subject +### 3.1.2 Subject The REQUIRED `sub` field both parameterizes over a specific agent, and acts as a namespace for how to interpret the [Command]. This is especially critical for two parts of the life cycle: 1. Specifying a particular `sub` (and thus `aud`) when [enqueuing new Tasks][enqueue] in a Receipt 2. Indexing Receipts for reverse lookup and memoization -### 3.1.4 Nonce +### 3.1.3 Audience + +The OPTIONAL `aud` field specified the intended recipitent of Invocation, otherwise the Audience MUST be assumed to the [Subject]. This is useful for message routing, command brokers, proxy execution, gateways, replicated state machines, and so on. + +### 3.1.4 Command + +The REQUIRED Command (`do`) field MUST contain a concrete, dispatchable message that can be sent to the Executor. The Command MUST define the shape of the data in the [Arguments]. + +### 3.1.5 Arguments + +The REQUIRED Arguments (`args`) field, MAY contain any parameters expected by the Command. The Subject MUST be considered the authority on the shape of this data. This field MUST be representable as a map or keyword list. + +UCAN capabilities provided in proofs MAY impose constraints on the type of Arguments allowed. + +### 3.1.6 Nonce The REQUIRED `nonce` field MUST include a random nonce. This field ensures that multiple (non-idempotent) invocations are unique. The nonce SHOULD be empty (`0x`) for Commands that are idempotent (such as deterministic Wasm modules or standards-abiding HTTP PUT requests). -### 3.1.5 Metadata +### 3.1.7 Metadata The `meta` field MAY include arbitrary metadata or extensable fields. For example, Wasm fuel, an internal job ID, references to GitHub Issues, and so on. This data MAY be used by the Executor. -### 3.1.6 Proofs +### 3.1.8 Proofs The `prf` field defines all [Delegation]s required to prove that this Invocation has an unbroken authorization chain. -### 3.1.7 Expiry +### 3.1.9 Expiry The OPTIONAL field `exp` defines when the Invocation SHOULD time out. This is both expressive (defines a timeout, which is a best practice), and prevents replays. -### 3.1.8 Issued At +### 3.1.10 Issued At The `iat` field MAY contain an issuance timestamp. This time SHOULD NOT be trusted; it is only a claim by the Invoker of their system time. System clocks often have clock skew, or a Byzantine Invoker could claim an arbitrary time. +### 3.1.11 Cause + +The OPTIONAL `cause` field is a provenance claim describing which [Receipt] requested it. This is helpful for tracking chains of Invocations. + ## 3.2 Task A Task is the subset of Invocation fields that uniquely determine the work to be performed[^subtype]. A Task MUST be unqiuely defined by the following fields: @@ -524,7 +536,6 @@ sequenceDiagram "body": "Let get coffee sometime and talk about UCAN Invocations!" }, "nonce": {"/": {"bytes": "TWFueSBopZ2h0IHdvcs"}}, - "mta": {}, "prf": [{"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}], "exp": 1697409438 } @@ -555,7 +566,7 @@ sequenceDiagram } } ``` - + # 4 Response A `Receipt` is a kind of Invocation used to attest to the result of another Invocation. A Receipt MUST be issued by the [Executor] (including by [Execution Proxy]). From bb52448c2900cdd002b8fe415784074f8ae8952d Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 17:37:06 +0200 Subject: [PATCH 106/127] Remove section numbers --- README.md | 187 +++++++++++++++++++++++++++--------------------------- 1 file changed, 93 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index 06ebffd2..c8f54e03 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,11 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [BCP 14] when, and only when, they appear in all capitals, as shown here. -# 0 Abstract +# Abstract UCAN Invocation defines a format for expressing the intention to execute delegated UCAN capabilities, and the attested receipts from an execution. -# 1 Introduction +# Introduction > Just because you can doesn't mean that you should > @@ -39,9 +39,9 @@ UCAN is a chained-capability format. A UCAN contains all of the information that Some teams have had success with UCAN directly for RPC when the intention is clear from context. This can be successful when there is more information on the channel than the UCAN itself (such as an HTTP path that a UCAN is sent to). However, capability invocation contains strictly more information than delegation: all of the authority of UCAN, plus the command to perform the task. -## 1.1 Intuition +## Intuition -## 1.1.1 Car Keys +### Car Keys Consider the following fictitious scenario: @@ -76,7 +76,7 @@ In the example above, steps ➌ and ➍ are qualitatively different: - Step ➌ grants authority (to drive the car) - Step ➍ is a _command_ to do so -## 1.1.2 Lazy vs Eager Evaluation +## Lazy vs Eager Evaluation In a referentially transparent setting, the description of a task is equivalent to having done so: a function and its results are interchangeable. [Programming languages with call-by-need semantics][Haskell] have shown that this can be an elegant programming model, especially for pure functions. However, _when_ something will run can sometimes be unclear. @@ -96,21 +96,21 @@ Delegating a capability is like the statement `message`. Task is akin to `messag However, there is clearly a distinction between passing a function and invoking it. The same is true for capabilities: delegating the authority to do something is not the same as asking for it to be done immediately, even if sometimes it's clear from context. -## 1.2 Public Resources +## Public Resources A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard requirement for initiating a flow (e.g. sign up). These cases typically involve a reference passed out of band (such as a web link). Due to [designation without authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain, but this SHOULD NOT be the default behavior. -## 1.3 Promise Pipelining +## Promise Pipelining [UCAN Promise] extends UCAN Invocation with [distributed promise pipelines]. Promises are helpful in a wide variety of situations for efficiency and convenience. Implementing UCAN Promises is RECOMMENDED. -## 1.4 Serialization +## Serialization UCAN Invocations MUST be encoded with some [IPLD] codec. [DAG-CBOR] is RECOMMENDED. -# 2 Concepts +# Concepts -## 2.1 Roles +## Roles Task adds two new roles to UCAN: invoker and executor. The existing UCAN delegator and delegate principals MUST persist to the invocation. @@ -119,17 +119,17 @@ Task adds two new roles to UCAN: invoker and executor. The existing UCAN delegat | `iss` | Delegator: transfer authority (active) | Invoker: request task (active) | | `aud` | Delegate: gain authority (passive) | Executor: perform task (active) | -### 2.1.1 Invoker +### Invoker The invoker signals to the executor that a task associated with a UCAN SHOULD be performed. The invoker MUST be the UCAN delegator. Their DID MUST be authenticated in the `iss` field of the contained UCAN. -### 2.1.2 Executor +### Executor The executor is directed to perform some task described in the UCAN invocation by the invoker. -## 2.2 Life Cycle +## Life Cycle At a very high level: @@ -145,7 +145,7 @@ erDiagram Receipt |o--|{ Task: enqueues ``` -## 2.3 Anatomy +## Anatomy | Concept | Description | |--------------|-----------------------------------------------------------------------------| @@ -200,7 +200,7 @@ flowchart RL Ran -->|provenance| Invocation ``` -# 3 Request +# Request A request for some work to be done (or to "exercise your authority") is an Invocation. @@ -228,14 +228,14 @@ As [noted in the introduction][lazy-vs-eager], there is a difference between a r Note that the Invocation MUST include the Signature envelope. An [Invocation Payload] on its own MUST NOT be considered a valid Invocation. -## 3.1 Invocation (Envelope) +## Invocation (Envelope) | Field | Type | Required | Description | |-------|--------------------|----------|-------------------------------------------------------| | `s` | `Signature` | Yes | A signature by the Payload's `iss` over the `p` field | | `p` | `SignaturePayload` | Yes | The content that was signed | -## 3.2 Signature Payload +## Signature Payload | Field | Type | Required | Description | |---------------------|---------------------|----------|--------------------------| @@ -247,7 +247,7 @@ The Signature Payload MUST contain a [Varsig] header, and the [Invocation Payloa 1. Commits the Signature to the cryptographic algorithms used 2. Describes how the paylaod was serialized before signing -## 3.3 Invocation Payload +## Invocation Payload The Invocation Payload attaches sender, receiver, and provenance to the [Task]. @@ -290,73 +290,78 @@ The shape of the `args` MUST be defined by the `do` field type. This is similar }) ``` -### 3.1.1 Issuer +### Agents + +#### Issuer The `iss` field MUST include the Issuer of the Invocation. This DID MUST match against the encoding signature. -### 3.1.2 Subject +#### Subject The REQUIRED `sub` field both parameterizes over a specific agent, and acts as a namespace for how to interpret the [Command]. This is especially critical for two parts of the life cycle: 1. Specifying a particular `sub` (and thus `aud`) when [enqueuing new Tasks][enqueue] in a Receipt 2. Indexing Receipts for reverse lookup and memoization -### 3.1.3 Audience +#### Audience The OPTIONAL `aud` field specified the intended recipitent of Invocation, otherwise the Audience MUST be assumed to the [Subject]. This is useful for message routing, command brokers, proxy execution, gateways, replicated state machines, and so on. -### 3.1.4 Command +### Task + +A Task is the subset of Invocation fields that uniquely determine the work to be performed[^subtype]. The nonce is important for distinguishing between non-idempotent executions of a Task by making the group together unique. + +A Task MUST be unqiuely defined by the following fields: + +- [Subject] +- [Command] +- [Arguments] +- [Nonce] + +[^subtype]: Which is to say: an Invocation is a subtype of Task + +#### Command The REQUIRED Command (`do`) field MUST contain a concrete, dispatchable message that can be sent to the Executor. The Command MUST define the shape of the data in the [Arguments]. -### 3.1.5 Arguments +### Arguments The REQUIRED Arguments (`args`) field, MAY contain any parameters expected by the Command. The Subject MUST be considered the authority on the shape of this data. This field MUST be representable as a map or keyword list. UCAN capabilities provided in proofs MAY impose constraints on the type of Arguments allowed. -### 3.1.6 Nonce +#### Nonce The REQUIRED `nonce` field MUST include a random nonce. This field ensures that multiple (non-idempotent) invocations are unique. The nonce SHOULD be empty (`0x`) for Commands that are idempotent (such as deterministic Wasm modules or standards-abiding HTTP PUT requests). -### 3.1.7 Metadata - -The `meta` field MAY include arbitrary metadata or extensable fields. For example, Wasm fuel, an internal job ID, references to GitHub Issues, and so on. This data MAY be used by the Executor. - -### 3.1.8 Proofs +### Proofs The `prf` field defines all [Delegation]s required to prove that this Invocation has an unbroken authorization chain. -### 3.1.9 Expiry - -The OPTIONAL field `exp` defines when the Invocation SHOULD time out. This is both expressive (defines a timeout, which is a best practice), and prevents replays. +### Provenance -### 3.1.10 Issued At +#### Cause -The `iat` field MAY contain an issuance timestamp. This time SHOULD NOT be trusted; it is only a claim by the Invoker of their system time. System clocks often have clock skew, or a Byzantine Invoker could claim an arbitrary time. +The OPTIONAL `cause` field is a provenance claim describing which [Receipt] requested it. This is helpful for tracking chains of Invocations. -### 3.1.11 Cause -The OPTIONAL `cause` field is a provenance claim describing which [Receipt] requested it. This is helpful for tracking chains of Invocations. +#### Expiry -## 3.2 Task +The OPTIONAL field `exp` defines when the Invocation SHOULD time out. This is both expressive (defines a timeout, which is a best practice), and prevents replays. -A Task is the subset of Invocation fields that uniquely determine the work to be performed[^subtype]. A Task MUST be unqiuely defined by the following fields: +#### Issued At -[^subtype]: Which is to say: an Invocation is a subtype of Task +The OPTIONAL `iat` field MAY contain an issuance timestamp. This time SHOULD NOT be trusted; it is only a claim by the Invoker of their system time. System clocks often have clock skew, or a Byzantine Invoker could claim an arbitrary time. -- [Subject] -- [Command] -- [Arguments] -- [Nonce] +#### Metadata -The nonce is important for distinguishing between non-idempotent executions of a Task by making the group together unique. +The OPTIONAL `meta` field MAY include arbitrary metadata or extensable fields. For example, Wasm fuel, an internal job ID, references to GitHub Issues, and so on. This data MAY be used by the Executor. -## 3.3 Attestation +## Attestation An Invocation MAY be used to attest to some information. This is in effect a statement to the Issuer (without Audience) that never expires. -## 3.4 Proof Chains +## Proof Chains A Task MUST include the entire [UCAN Delegation] proof chain in the `prf` field. The chain MUST form a direct line of authority, starting with the delegation with an `aud` that matches the Invoker, and ending with a delegation where the `iss` matches the `sub`. The `sub` throughout MUST match the `aud` of the Invocation. @@ -414,7 +419,7 @@ flowchart RL prf --> Delegations ``` -### 3.4.1 Proxied Proof Paths with `ucan/proxy` +### Proxied Proof Paths with `ucan/proxy` Beyond [attenuation], [`ucan/proxy`] MAY be used to connect otherwise disjoint parts of an authorization network. The motivation is to express the intention of automatically re-delegating (or "forwarding") authority to another agent if you are offline, while retaining the ability to [revoke] that link. The clear use case is linking user devices, but also has applications for PoLA "cold" root/admin keys for servers. @@ -473,9 +478,9 @@ sequenceDiagram } ``` -## 3.5 Examples +## Examples -### 3.5.1 Interacting with an HTTP API +### Interacting with an HTTP API ```js // DAG-JSON @@ -516,7 +521,7 @@ sequenceDiagram } ``` -### 3.5.2 Sending Email +### Sending Email ```js // DAG-JSON @@ -543,7 +548,7 @@ sequenceDiagram } ``` -### 3.5.3 Inline WebAssembly +### Inline WebAssembly ```js { @@ -567,7 +572,7 @@ sequenceDiagram } ``` -# 4 Response +# Response A `Receipt` is a kind of Invocation used to attest to the result of another Invocation. A Receipt MUST be issued by the [Executor] (including by [Execution Proxy]). @@ -580,14 +585,14 @@ Receipts MUST use the same-or-higher version number as the [Invocation] that the | `s` | `Signature` | Yes | Signature (bytes or struct) of the `pld` field, which MUST be interpreted as the `pld.h` field | | `p` | `&Payload` | Yes | The data being signed over | -## 3.2 Signature Payload +## Signature Payload | Field | Type | Required | Description | |---------------------|------------------|----------|------------------------------------------------------| | `h` | `VarsigHeader` | Yes | [Varsig] header that describes the outer `sig` field | | `ucan/r/1.0.0-rc.1` | `ReceiptPayload` | Yes | Fields unique to the Receipt | -## 4.2 Receipt Payload +## Receipt Payload Receipt Payloads MUST conform to the following shape: @@ -603,7 +608,7 @@ Receipt Payloads MUST conform to the following shape: A few of these fields warrant further comment below. -### 4.1 Result +### Result A Result records the output of the [Task], as well as its success or failure state. A Result MUST be formatted as map with a single `tag` field. @@ -625,7 +630,7 @@ A Result records the output of the [Task], as well as its success or failure sta } ``` -## 4.2 Next Task(s) +## Next Task(s) The result of an [Invocation] MAY include a request for further actions to be performed. This is a process of requesting that the invoker "enqueue" a new Task. This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. @@ -633,7 +638,7 @@ Enqueued [Task]s describe requests for future work to be performed. They SHOULD All [Task]s in an [enqueue] array MUST be treated as concurrent, unless explicit data dependencies between them exist via [UCAN Promise]s. -## 4.3 Examples +## Examples ``` js // DAG-JSON @@ -646,13 +651,13 @@ All [Task]s in an [enqueue] array MUST be treated as concurrent, unless explicit "ran": {"/": "bafkreictzcfwelyww7zmjkl5nptyot24oilky2bppw42nui2acozhfmzqa"}, "out": {"ok": 42}, "iat": 1702907627, - "prf": [], + "prf": [{"/": "bafkreig3e34gbe65q4axx7bcsq3aei7urggprx7uynpltkhp4nclqzebbu"}], } } } ``` -# 5 Proxy Execution +# Proxy Execution If the Receipt Issuer is not identical to the `aud` field of Invocation referenced in the `ran` field, a [Delegation] proof chain SHOULD be included. If a chain is present, it MUST show that such a proxy execution was authorized by the original listed `aud` Agent. @@ -689,7 +694,7 @@ const receipt = { } ``` -## 5.1 Examples +## Examples ``` js // DAG-JSON @@ -697,26 +702,18 @@ const receipt = { "s": {"/": {"bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU"}}, "p": { "h": {"/": {"bytes": "NBIFEgEAcQ"}}, - "ucan/i/1.0.0-rc.1": { + "ucan/r/1.0.0-rc.1": { "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", - "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", - "do": "ucan/receipt", - "args": { - "ran": {"/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu"}, - "out": {"ok": ["bob@example.com", "alice@example.com"]} - }, - "meta": { - "retry-count": 2, - "total-time": [400, "hours"] - }, + "ran": {"/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu"}, + "out": {"ok": ["bob@example.com", "alice@example.com"]} "prf": [] } } } ``` -# 6 Prior Art +# Prior Art [ucanto RPC] from [DAG House] is a production system that uses UCAN as the basis for an RPC layer. @@ -752,28 +749,30 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen -[Action]: #31-action -[Arguments]: #312-arguments -[Command]: #311-command -[Execution Proxy]: #423-proxy-execution -[Executor]: #212-executor -[Expiry]: #317-expiry -[Invocation Payload]: #331-invocation-payload -[Invocation Signature]: #331-invocation-envelope -[Invocation]: #33-invocation -[Invoker]: #211-invoker -[Issued At]: #318-issued-at -[Metadata]: #315-metadata -[Nonce]: #314-nonce -[Proofs]: #316-proofs -[Receipt Payload]: #42-receipt-payload -[Receipt]: #41-receipt-envelope -[Response]: #4-response -[Result]: #421-result -[Subject]: #313-subject -[Task]: #32-task -[enqueue]: #422-enqueue -[lazy-vs-eager]: #112-lazy-vs-eager-evaluation +[Issuer]: #issuer +[Audience]: #audience +[Action]: #action +[Arguments]: #arguments +[Command]: #command +[Execution Proxy]: #proxy-execution +[Executor]: #executor +[Expiry]: #expiry +[Invocation Payload]: #invocation-payload +[Invocation Signature]: #invocation-envelope +[Invocation]: #invocation +[Invoker]: #invoker +[Issued At]: #issued-at +[Metadata]: #metadata +[Nonce]: #nonce +[Proofs]: #proofs +[Receipt Payload]: #receipt-payload +[Receipt]: #receipt-envelope +[Response]: #response +[Result]: #result +[Subject]: #subject +[Task]: #task +[enqueue]: #enqueue +[lazy-vs-eager]: #lazy-vs-eager-evaluation From 277b1d3761362820a492acda2ab9134af632a82d Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 17:38:20 +0200 Subject: [PATCH 107/127] Add missing link --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c8f54e03..38f8bd37 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,7 @@ flowchart RL Ran -->|provenance| Invocation ``` -# Request +# Request (Invocation) A request for some work to be done (or to "exercise your authority") is an Invocation. @@ -572,7 +572,7 @@ sequenceDiagram } ``` -# Response +# Response (Receipt) A `Receipt` is a kind of Invocation used to attest to the result of another Invocation. A Receipt MUST be issued by the [Executor] (including by [Execution Proxy]). @@ -749,10 +749,10 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen -[Issuer]: #issuer -[Audience]: #audience [Action]: #action [Arguments]: #arguments +[Audience]: #audience +[Cause]: #cause [Command]: #command [Execution Proxy]: #proxy-execution [Executor]: #executor @@ -762,6 +762,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Invocation]: #invocation [Invoker]: #invoker [Issued At]: #issued-at +[Issuer]: #issuer [Metadata]: #metadata [Nonce]: #nonce [Proofs]: #proofs From 32ab1c10a0e787ffe30bab51a5fac559fa6d043b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 17:41:12 +0200 Subject: [PATCH 108/127] Add diagram for symmetry --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 38f8bd37..c9af8895 100644 --- a/README.md +++ b/README.md @@ -578,6 +578,28 @@ A `Receipt` is a kind of Invocation used to attest to the result of another Invo **NB: a Receipt does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. +``` mermaid +flowchart TD + subgraph Receipt + SignatureBytes["Signature (raw bytes)"] + + subgraph SigPayload ["Signature Payload"] + VarsigHeader["Varsig Header"] + + subgraph InvocationPayload ["Receipt Payload"] + iss + sub + ran + out + prf + etc["..."] + end + end + end + + ran -.->|CID| Invocation +``` + Receipts MUST use the same-or-higher version number as the [Invocation] that they reference. | Field | Type | Required | Description | From ef93371f9f08ff55c04bfd259f01c62f4a160062 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 17:44:03 +0200 Subject: [PATCH 109/127] Add same --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c9af8895..7d12a0b8 100644 --- a/README.md +++ b/README.md @@ -218,10 +218,13 @@ flowchart TD do args prf + cause etc["..."] end end end + + cause -.->|CID| Receipt ``` As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is a request to the [Executor] to perform the enclosed [Task]. [Invocation Payload]s are not executable until they have been signed and [Delegation] proofs validated. @@ -789,7 +792,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Nonce]: #nonce [Proofs]: #proofs [Receipt Payload]: #receipt-payload -[Receipt]: #receipt-envelope +[Receipt]: #response-receipt [Response]: #response [Result]: #result [Subject]: #subject From 547123ad87128a5367235ec8c60fa8ff965742c0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 17:45:04 +0200 Subject: [PATCH 110/127] consistency --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7d12a0b8..766ce22f 100644 --- a/README.md +++ b/README.md @@ -488,7 +488,7 @@ sequenceDiagram ```js // DAG-JSON { - "s": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, + "s": {"/": {"bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, "p": { "h": {"/": {"bytes": "NBIFEgEAcQ"}}, "ucan/i/1.0.0-rc.1": { @@ -529,7 +529,7 @@ sequenceDiagram ```js // DAG-JSON { - "s": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, + "s": {"/": {"bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, "p": { "h": {"/": {"bytes": "NBIFEgEAcQ"}}, "ucan/i/1.0.0-rc.1": { @@ -555,7 +555,7 @@ sequenceDiagram ```js { - "s": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, + "s": {"/": {"bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, "p": { "h": {"/": {"bytes": "NBIFEgEAcQ"}}, "ucan/i/1.0.0-rc.1": { From f6d0eabb64aa19981e14fddd99885f215713aed0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 17:46:19 +0200 Subject: [PATCH 111/127] Broken internal link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 766ce22f..c6501c3b 100644 --- a/README.md +++ b/README.md @@ -784,7 +784,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Expiry]: #expiry [Invocation Payload]: #invocation-payload [Invocation Signature]: #invocation-envelope -[Invocation]: #invocation +[Invocation]: #request-invocation [Invoker]: #invoker [Issued At]: #issued-at [Issuer]: #issuer From 7857352c8d42130fa4c1324860684c1faba5fc0a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 17:47:29 +0200 Subject: [PATCH 112/127] Flip titles --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c6501c3b..debe041a 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,7 @@ flowchart RL Ran -->|provenance| Invocation ``` -# Request (Invocation) +# Invocation (Request) A request for some work to be done (or to "exercise your authority") is an Invocation. @@ -575,7 +575,7 @@ sequenceDiagram } ``` -# Response (Receipt) +# Receipt (Response) A `Receipt` is a kind of Invocation used to attest to the result of another Invocation. A Receipt MUST be issued by the [Executor] (including by [Execution Proxy]). @@ -784,7 +784,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Expiry]: #expiry [Invocation Payload]: #invocation-payload [Invocation Signature]: #invocation-envelope -[Invocation]: #request-invocation +[Invocation]: #invocation-request [Invoker]: #invoker [Issued At]: #issued-at [Issuer]: #issuer @@ -792,7 +792,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Nonce]: #nonce [Proofs]: #proofs [Receipt Payload]: #receipt-payload -[Receipt]: #response-receipt +[Receipt]: #receipt-response [Response]: #response [Result]: #result [Subject]: #subject From 73cb161e278f90cb5ed0ba50a9a9055534015ee6 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 17:49:12 +0200 Subject: [PATCH 113/127] Spellcheck --- .github/workflows/words-to-ignore.txt | 1 + README.md | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index ae4a0d0b..ee8b62cd 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -78,6 +78,7 @@ invoker js-num memoization multiformat +namespace outmost parallelize parameterizing diff --git a/README.md b/README.md index debe041a..cb05511c 100644 --- a/README.md +++ b/README.md @@ -245,10 +245,10 @@ Note that the Invocation MUST include the Signature envelope. An [Invocation Pay | `h` | `VarsigHeader` | Yes | The Varsig header | | `ucan/i/1.0.0-rc.1` | `InvocationPayload` | Yes | The [Invocation Payload] | -The Signature Payload MUST contain a [Varsig] header, and the [Invocation Payload]. The Varsig header MUST describe the cryptographic configuration used to format and sign the [Invocaion Payload]. This is important in order to: +The Signature Payload MUST contain a [Varsig] header, and the [Invocation Payload]. The Varsig header MUST describe the cryptographic configuration used to format and sign the [Invocation Payload]. This is important in order to: 1. Commits the Signature to the cryptographic algorithms used -2. Describes how the paylaod was serialized before signing +2. Describes how the payload was serialized before signing ## Invocation Payload @@ -308,13 +308,13 @@ The REQUIRED `sub` field both parameterizes over a specific agent, and acts as a #### Audience -The OPTIONAL `aud` field specified the intended recipitent of Invocation, otherwise the Audience MUST be assumed to the [Subject]. This is useful for message routing, command brokers, proxy execution, gateways, replicated state machines, and so on. +The OPTIONAL `aud` field specified the intended recipient of Invocation, otherwise the Audience MUST be assumed to the [Subject]. This is useful for message routing, command brokers, proxy execution, gateways, replicated state machines, and so on. ### Task A Task is the subset of Invocation fields that uniquely determine the work to be performed[^subtype]. The nonce is important for distinguishing between non-idempotent executions of a Task by making the group together unique. -A Task MUST be unqiuely defined by the following fields: +A Task MUST be uniquely defined by the following fields: - [Subject] - [Command] From 61c00e7c3e7d01a331c5c2952ba9b9b554e421c3 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 17:50:14 +0200 Subject: [PATCH 114/127] Spelling --- .github/workflows/words-to-ignore.txt | 1 + README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index ee8b62cd..1cad8012 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -81,6 +81,7 @@ multiformat namespace outmost parallelize +parameterizes parameterizing pipelined pipelining diff --git a/README.md b/README.md index cb05511c..5beaa105 100644 --- a/README.md +++ b/README.md @@ -358,7 +358,7 @@ The OPTIONAL `iat` field MAY contain an issuance timestamp. This time SHOULD NOT #### Metadata -The OPTIONAL `meta` field MAY include arbitrary metadata or extensable fields. For example, Wasm fuel, an internal job ID, references to GitHub Issues, and so on. This data MAY be used by the Executor. +The OPTIONAL `meta` field MAY include arbitrary metadata or extensible fields. For example, Wasm fuel, an internal job ID, references to GitHub Issues, and so on. This data MAY be used by the Executor. ## Attestation From c596f04bbaf56c58df76bb578c6dfd53bb88bece Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 17:57:57 +0200 Subject: [PATCH 115/127] Fix ipldsch --- invocation.ipldsch | 30 ++++++++++++++---------------- receipt.ipldsch | 9 +++++++-- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/invocation.ipldsch b/invocation.ipldsch index 44d7d91d..6deda743 100644 --- a/invocation.ipldsch +++ b/invocation.ipldsch @@ -1,29 +1,27 @@ type Invocation struct { - inv &InvocationPayload - sig Signature + p SignaturePayload + s Signature +} + +type SignaturePayload { + h VarsigHeader + i Invocation } type InvocationPayload struct { uiv SemVer iss DID - aud DID + sub DID + aud optional DID - run &Task - cse optional &Receipt -} + do Command + args {String : Any} + nonce String -type Task struct { - act &Command - mta {String : Any} + meta {String : Any} prf [&Delegation] exp optional Integer -} - -type Action { - cmd Command - arg {String : Any} - nnc String - sub optional DID + cause optional &Receipt } diff --git a/receipt.ipldsch b/receipt.ipldsch index 61a5f9ca..27c5f539 100644 --- a/receipt.ipldsch +++ b/receipt.ipldsch @@ -1,6 +1,11 @@ type Receipt { - pld &Receipt - sig &Signature + s Signature + p SignaturePayload +} + +type SignaturePayload { + h VarsigHeader + r ReceiptPayload } type ReceiptPayload struct { From 8448338705f0af41599963bf6bdd895c19d21f7e Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 23:33:34 -0700 Subject: [PATCH 116/127] Update to latest spec format --- README.md | 473 +++++++++++------------------------------------------- 1 file changed, 98 insertions(+), 375 deletions(-) diff --git a/README.md b/README.md index 5beaa105..0f61f554 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,36 @@ -# UCAN Invocation Specification v1.0.0-rc. 1 +# UCAN Invocation Specification +## Version 1.0.0-rc. 1 ## Editors +[Editors]: #editors -- [Brooklyn Zelenka], [Fission] -- [Irakli Gozalishvili], [DAG House] +- [Brooklyn Zelenka], [Witchcraft Software] +- [Irakli Gozalishvili], [Protocol Labs] ## Authors +[Authors]: #authors -- [Brooklyn Zelenka], [Fission] -- [Irakli Gozalishvili], [DAG House] -- [Zeeshan Lakhani], [Fission] +- [Brooklyn Zelenka], [Witchcraft Software] +- [Irakli Gozalishvili], [Protocol Labs] +- [Zeeshan Lakhani], [Oxide Computer] -## Depends On +# Dependencies +[Dependencies]: #dependencies -- [IPLD] -- [DID] - [UCAN Delegation] ## Language +[Language]: #language The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [BCP 14] when, and only when, they appear in all capitals, as shown here. # Abstract +[Abstract]: #abstract UCAN Invocation defines a format for expressing the intention to execute delegated UCAN capabilities, and the attested receipts from an execution. # Introduction +[Introduction]: #introduction > Just because you can doesn't mean that you should > @@ -33,15 +38,17 @@ UCAN Invocation defines a format for expressing the intention to execute delegat > When authorization is communicated without such context, it's like receiving a key in the mail with no hint about what to do with it [...] After an object receives this message, she can invoke arg if she chooses, but why would she ever choose to do so? > -> Mark Miller, [E-lang Mailing List, 2000 Oct 18] +> [Mark Miller], [E-lang Mailing List, 2000 Oct 18] UCAN is a chained-capability format. A UCAN contains all of the information that one would need to perform some task, and the provable authority to do so. This begs the question: can UCAN be used directly as an RPC language? Some teams have had success with UCAN directly for RPC when the intention is clear from context. This can be successful when there is more information on the channel than the UCAN itself (such as an HTTP path that a UCAN is sent to). However, capability invocation contains strictly more information than delegation: all of the authority of UCAN, plus the command to perform the task. ## Intuition +[Intuition]: #intuition ### Car Keys +[Car Keys]: #car-keys Consider the following fictitious scenario: @@ -77,6 +84,7 @@ In the example above, steps ➌ and ➍ are qualitatively different: - Step ➍ is a _command_ to do so ## Lazy vs Eager Evaluation +[Lazy vs Eager Evaluation]: #lazy-vs-eager-evaluation In a referentially transparent setting, the description of a task is equivalent to having done so: a function and its results are interchangeable. [Programming languages with call-by-need semantics][Haskell] have shown that this can be an elegant programming model, especially for pure functions. However, _when_ something will run can sometimes be unclear. @@ -97,20 +105,20 @@ Delegating a capability is like the statement `message`. Task is akin to `messag However, there is clearly a distinction between passing a function and invoking it. The same is true for capabilities: delegating the authority to do something is not the same as asking for it to be done immediately, even if sometimes it's clear from context. ## Public Resources +[Public Resources]: #public-resources A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard requirement for initiating a flow (e.g. sign up). These cases typically involve a reference passed out of band (such as a web link). Due to [designation without authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain, but this SHOULD NOT be the default behavior. ## Promise Pipelining +[Promise Pipelining]: #promise-pipelining -[UCAN Promise] extends UCAN Invocation with [distributed promise pipelines]. Promises are helpful in a wide variety of situations for efficiency and convenience. Implementing UCAN Promises is RECOMMENDED. - -## Serialization - -UCAN Invocations MUST be encoded with some [IPLD] codec. [DAG-CBOR] is RECOMMENDED. +[UCAN Promise] extends UCAN Invocation with [distributed promise pipelines]. Promises are helpful in a wide variety of situations for efficiency and convenience. Implementations supporting UCAN Promises is RECOMMENDED. # Concepts +[Concepts]: #concepts ## Roles +[Roles]: #roles Task adds two new roles to UCAN: invoker and executor. The existing UCAN delegator and delegate principals MUST persist to the invocation. @@ -120,16 +128,19 @@ Task adds two new roles to UCAN: invoker and executor. The existing UCAN delegat | `aud` | Delegate: gain authority (passive) | Executor: perform task (active) | ### Invoker +[Invoker]: #invoker The invoker signals to the executor that a task associated with a UCAN SHOULD be performed. The invoker MUST be the UCAN delegator. Their DID MUST be authenticated in the `iss` field of the contained UCAN. ### Executor +[Executor]: #executor The executor is directed to perform some task described in the UCAN invocation by the invoker. ## Life Cycle +[Life Cycle]: #life-cycle At a very high level: @@ -146,61 +157,13 @@ erDiagram ``` ## Anatomy +[Anatomy]: #anatomy | Concept | Description | |--------------|-----------------------------------------------------------------------------| -| [Command] | (Deferred) function application; a description of work to be performed | +| [Command] | Function application; a description of work to be performed | | [Task] | Contextual information for a [Command], such as resource limits | | [Invocation] | A request to perform some [Task] based on [delegated][Delegation] authority | -| [Result] | The success value or error information from the run [Invocation] | -| [Receipt] | The return value from an [Invocation], which may [enqueue] more tasks | - -``` mermaid -flowchart RL - subgraph Invocation - direction LR - subgraph InvocationPayload - subgraph Task - direction TB - - Command - DelegationProofs[Delegation Proofs] - end - end - - Inv1Sig[Signature] - end - - subgraph Receipt - subgraph ReceiptPayload - Ran - Result - Enqueue - end - - RecSig[Signature] - end - - subgraph Invocation2["(Next) Invocation"] - direction RL - - subgraph InvocationPayload2[Invocation Payload] - subgraph Task2 - Command2["Command"] - DelegationProofs2[Delegation Proofs] - end - - Cause - end - - Inv2Sig[Signature] - end - - Cause -->|provenance| Enqueue - Ran -->|provenance| Invocation -``` - -# Invocation (Request) A request for some work to be done (or to "exercise your authority") is an Invocation. @@ -218,7 +181,7 @@ flowchart TD do args prf - cause + cause["cause (optional)"] etc["..."] end end @@ -231,49 +194,41 @@ As [noted in the introduction][lazy-vs-eager], there is a difference between a r Note that the Invocation MUST include the Signature envelope. An [Invocation Payload] on its own MUST NOT be considered a valid Invocation. -## Invocation (Envelope) - -| Field | Type | Required | Description | -|-------|--------------------|----------|-------------------------------------------------------| -| `s` | `Signature` | Yes | A signature by the Payload's `iss` over the `p` field | -| `p` | `SignaturePayload` | Yes | The content that was signed | +# [UCAN Envelope] Configuration +[UCAN Envelope Configuration]: #ucan-envelope-configuration -## Signature Payload - -| Field | Type | Required | Description | -|---------------------|---------------------|----------|--------------------------| -| `h` | `VarsigHeader` | Yes | The Varsig header | -| `ucan/i/1.0.0-rc.1` | `InvocationPayload` | Yes | The [Invocation Payload] | - -The Signature Payload MUST contain a [Varsig] header, and the [Invocation Payload]. The Varsig header MUST describe the cryptographic configuration used to format and sign the [Invocation Payload]. This is important in order to: - -1. Commits the Signature to the cryptographic algorithms used -2. Describes how the payload was serialized before signing +## Type Tag +[Type Tag]: #type-tag + +The UCAN envelope's payload tag MUST be `ucan/inv@1.0.0-rc.1`. ## Invocation Payload +[Invocation Payload]: #invocation-payload The Invocation Payload attaches sender, receiver, and provenance to the [Task]. -| Field | Type | Required | Description | | -|-------------|---------|--------------------|-------------|--------------------------------------------------------------------| -| [Issuer] | `iss` | `DID` | Yes | The DID of the [Invoker] | -| [Subject] | `sub` | `DID` | Yes | The [Subject] being invoked | -| [Audience] | `aud` | `DID` | No | The DID of the intended [Executor] if different from the [Subject] | -| [Command] | `do` | `String` | Yes | The [Command] | -| [Arguments] | `args` | `{String : Any}` | Yes | The [Command]'s [Arguments] | -| [Proofs] | `prf` | `[&Delegation]` | Yes | [Delegation]s that prove the chain of authority | -| [Metadata] | `meta` | `{String : Any}` | No | Arbitrary [Metadata] | -| [Nonce] | `nonce` | `Bytes` | No | A unique, random nonce | -| [Expiry] | `exp` | `Integer`[^js-num] | No | The timestamp at which the Invocation becomes invalid | -| [Issued At] | `iat` | `Integer`[^js-num] | No | The timestamp at which the Invocation was created | -| [Cause] | `cause` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | - -The shape of the `args` MUST be defined by the `do` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. Using the JavaScript analogy from the introduction, an Action is similar to wrapping a call in a closure: +| Field | Type | Required | Description | +|---------|--------------------|----------|--------------------------------------------------------------------| +| `iss` | `DID` | Yes | The DID of the [Invoker] | +| `sub` | `DID` | Yes | The [Subject] being invoked | +| `aud` | `DID` | No | The DID of the intended [Executor] if different from the [Subject] | +| `cmd` | `String` | Yes | The [Command] | +| `args` | `{String : Any}` | Yes | The [Command]'s [Arguments] | +| `prf` | `[&Delegation]` | Yes | [Delegation]s that prove the chain of authority | +| `meta` | `{String : Any}` | No | Arbitrary [Metadata] | +| `nonce` | `Bytes` | No | A unique, random nonce | +| `exp` | `Integer`[^js-num] | No | The timestamp at which the Invocation becomes invalid | +| `iat` | `Integer`[^js-num] | No | The timestamp at which the Invocation was created | +| `cause` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | + +[^js-num]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined as a [IEEE-754] double-precision floating point number, which has a 53-bit significand. + +The shape of the `args` MUST be defined by the `cmd` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. Using the JavaScript analogy from the introduction, an Action is similar to wrapping a call in a closure: ```js // Command { - "do": "msg/send", + "cmd": "/msg/send", "args": { "from": "mailto:alice@example.com", "to": [ "bob@example.com", "carol@example.com" ], @@ -294,12 +249,15 @@ The shape of the `args` MUST be defined by the `do` field type. This is similar ``` ### Agents +[Agents]: #agents #### Issuer +[Issuer]: #issuer The `iss` field MUST include the Issuer of the Invocation. This DID MUST match against the encoding signature. #### Subject +[Subject]: #subject The REQUIRED `sub` field both parameterizes over a specific agent, and acts as a namespace for how to interpret the [Command]. This is especially critical for two parts of the life cycle: @@ -307,64 +265,76 @@ The REQUIRED `sub` field both parameterizes over a specific agent, and acts as a 2. Indexing Receipts for reverse lookup and memoization #### Audience +[Audience]: #audience The OPTIONAL `aud` field specified the intended recipient of Invocation, otherwise the Audience MUST be assumed to the [Subject]. This is useful for message routing, command brokers, proxy execution, gateways, replicated state machines, and so on. ### Task +[Task]: #task -A Task is the subset of Invocation fields that uniquely determine the work to be performed[^subtype]. The nonce is important for distinguishing between non-idempotent executions of a Task by making the group together unique. +A Task is the subset of Invocation fields that uniquely determine the work to be performed. The nonce is important for distinguishing between non-idempotent executions of a Task by making the group together unique. -A Task MUST be uniquely defined by the following fields: +A Task MUST be uniquely defined by a Task ID that is the CID of the following fields: - [Subject] - [Command] - [Arguments] - [Nonce] -[^subtype]: Which is to say: an Invocation is a subtype of Task +Tasks that describe pure functions — or other strategies like fan-out racing — SHOULD have the same Task ID by using the same nonce. #### Command +[Command]: #command -The REQUIRED Command (`do`) field MUST contain a concrete, dispatchable message that can be sent to the Executor. The Command MUST define the shape of the data in the [Arguments]. +The REQUIRED Command (`cmd`) field MUST contain a concrete, dispatchable message that can be sent to the Executor. The Command MUST define the shape of the data in the [Arguments]. ### Arguments +[Arguments]: #arguments The REQUIRED Arguments (`args`) field, MAY contain any parameters expected by the Command. The Subject MUST be considered the authority on the shape of this data. This field MUST be representable as a map or keyword list. -UCAN capabilities provided in proofs MAY impose constraints on the type of Arguments allowed. +The Arguments MUST pass validation of the Policies on all of the [UCAN Delegations][UCAN Delegation] in the [Proofs] field. If any Policy reports failure, the Invocation MUST be rejected. #### Nonce +[Nonce]: #nonce The REQUIRED `nonce` field MUST include a random nonce. This field ensures that multiple (non-idempotent) invocations are unique. The nonce SHOULD be empty (`0x`) for Commands that are idempotent (such as deterministic Wasm modules or standards-abiding HTTP PUT requests). ### Proofs +[Proofs]: #proofs The `prf` field defines all [Delegation]s required to prove that this Invocation has an unbroken authorization chain. ### Provenance +[Provenance]: #provenance #### Cause +[Cause]: #cause The OPTIONAL `cause` field is a provenance claim describing which [Receipt] requested it. This is helpful for tracking chains of Invocations. - -#### Expiry +#### Expiration +[Expiration]: #expiration The OPTIONAL field `exp` defines when the Invocation SHOULD time out. This is both expressive (defines a timeout, which is a best practice), and prevents replays. #### Issued At +[Issued At]: #issued-at The OPTIONAL `iat` field MAY contain an issuance timestamp. This time SHOULD NOT be trusted; it is only a claim by the Invoker of their system time. System clocks often have clock skew, or a Byzantine Invoker could claim an arbitrary time. #### Metadata +[Metadata]: #metadata The OPTIONAL `meta` field MAY include arbitrary metadata or extensible fields. For example, Wasm fuel, an internal job ID, references to GitHub Issues, and so on. This data MAY be used by the Executor. ## Attestation +[Attestation]: #attestation An Invocation MAY be used to attest to some information. This is in effect a statement to the Issuer (without Audience) that never expires. ## Proof Chains +[Proof Chains]: #proof-chains A Task MUST include the entire [UCAN Delegation] proof chain in the `prf` field. The chain MUST form a direct line of authority, starting with the delegation with an `aud` that matches the Invoker, and ending with a delegation where the `iss` matches the `sub`. The `sub` throughout MUST match the `aud` of the Invocation. @@ -422,79 +392,21 @@ flowchart RL prf --> Delegations ``` -### Proxied Proof Paths with `ucan/proxy` - -Beyond [attenuation], [`ucan/proxy`] MAY be used to connect otherwise disjoint parts of an authorization network. The motivation is to express the intention of automatically re-delegating (or "forwarding") authority to another agent if you are offline, while retaining the ability to [revoke] that link. The clear use case is linking user devices, but also has applications for PoLA "cold" root/admin keys for servers. - -The `ucan/proxy` Command MAY be used to substitute into any delegation chain. It "forwards" whatever is later in the chain, in effect swapping out the `iss` field. `ucan/proxy` MUST NOT change the Ability it re-delegates. It MAY be scoped to a particular scheme or attach additional caveats. - -``` js -// Anything -{ - "sub": "did:web:example.com", - "can": "ucan/proxy", - "cond": [], - // ... -} -``` - -``` mermaid -sequenceDiagram - actor Alice - actor Bob - actor Carol - actor Dan - - autonumber - - Note over Alice, Dan: Delegation Setup - Bob -->> Carol: Delegate(ucan/proxy) - Alice -->> Bob: Delegate(crud/create, dns:example.com) - Carol -->> Dan: Delegate(crud/create, dns:example.com) - - Note over Alice, Dan: Invoke - Dan ->> Alice: Invoke(crud/create, dns:example.com, txt="hi", proof: [➋,➊,➌]) - - Note over Alice, Dan: Delegation path in ➍ - autonumber 2 - Alice -->> Bob: Delegate(crud/create, dns:example.com) - - autonumber 1 - rect rgb(127, 127, 127) - Bob -->> Carol: Delegate(ucan/proxy) - end - - autonumber 3 - Carol -->> Dan: Delegate(crud/create, dns:example.com) -``` - -```js -// Only DNS resources -{ - "sub": "did:web:example.com", - "can": "ucan/proxy", - "args": { - "scheme": "dns" - } - "if": [], - // ... -} -``` - ## Examples +[Examples]: #examples ### Interacting with an HTTP API ```js // DAG-JSON -{ - "s": {"/": {"bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, - "p": { +[ + {"/": {"bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, + { "h": {"/": {"bytes": "NBIFEgEAcQ"}}, "ucan/i/1.0.0-rc.1": { "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", - "do": "crud/create", + "cmd": "/crud/create", "args": { "uri": "https://example.com/blog/posts", "headers": { @@ -521,22 +433,22 @@ sequenceDiagram } } -} +] ``` ### Sending Email ```js // DAG-JSON -{ - "s": {"/": {"bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, - "p": { +[ + {"/": {"bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, + { "h": {"/": {"bytes": "NBIFEgEAcQ"}}, "ucan/i/1.0.0-rc.1": { "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", - "do": "msg/send", + "cmd": "/msg/send", "args": { "from": "mailto:akiko@example.com", "to": [ "boris@example.com", "carol@example.com" ], @@ -548,15 +460,15 @@ sequenceDiagram "exp": 1697409438 } } -} +] ``` ### Inline WebAssembly ```js -{ - "s": {"/": {"bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, - "p": { +[ + {"/": {"bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}}, + { "h": {"/": {"bytes": "NBIFEgEAcQ"}}, "ucan/i/1.0.0-rc.1": { "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", @@ -564,7 +476,7 @@ sequenceDiagram "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", "meta": {"fuel": 999999}, "nonce": {"/": {"bytes": ""}}, // NOTE: as stated above, idempotent Actions should always have the same nonce - "do": "wasm/run", + "cmd": "/wasm/run", "args": { "mod": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==", "fun": "add_one", @@ -572,173 +484,11 @@ sequenceDiagram } } } -} -``` - -# Receipt (Response) - -A `Receipt` is a kind of Invocation used to attest to the result of another Invocation. A Receipt MUST be issued by the [Executor] (including by [Execution Proxy]). - -**NB: a Receipt does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. - -``` mermaid -flowchart TD - subgraph Receipt - SignatureBytes["Signature (raw bytes)"] - - subgraph SigPayload ["Signature Payload"] - VarsigHeader["Varsig Header"] - - subgraph InvocationPayload ["Receipt Payload"] - iss - sub - ran - out - prf - etc["..."] - end - end - end - - ran -.->|CID| Invocation -``` - -Receipts MUST use the same-or-higher version number as the [Invocation] that they reference. - -| Field | Type | Required | Description | -|-------|-------------|----------|------------------------------------------------------------------------------------------------| -| `s` | `Signature` | Yes | Signature (bytes or struct) of the `pld` field, which MUST be interpreted as the `pld.h` field | -| `p` | `&Payload` | Yes | The data being signed over | - -## Signature Payload - -| Field | Type | Required | Description | -|---------------------|------------------|----------|------------------------------------------------------| -| `h` | `VarsigHeader` | Yes | [Varsig] header that describes the outer `sig` field | -| `ucan/r/1.0.0-rc.1` | `ReceiptPayload` | Yes | Fields unique to the Receipt | - -## Receipt Payload - -Receipt Payloads MUST conform to the following shape: - -| Field | Type | Required | Description | -|--------|--------------------|----------|------------------------------------------------------------------------------------| -| `iss` | `DID` | Yes | The DID of the Executor | -| `ran` | `&Invocation` | Yes | A link to the [Invocation] that the Receipt is for | -| `out` | `Result` | Yes | The value output of the invocation in [Result] format | -| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation | -| `next` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue | -| `meta` | `{String : Any}` | Yes | Additional data about the receipt | -| `iat` | `Integer`[^js-num] | No | The UTC Unix timestamp at which the Receipt was issued | - -A few of these fields warrant further comment below. - -### Result - -A Result records the output of the [Task], as well as its success or failure state. A Result MUST be formatted as map with a single `tag` field. - -| Tag | Value | Description | -|---------|------------------|-------------------------------------------------| -| `ok` | `Any` | The successfully returned data from a [Command] | -| `error` | `{String : Any}` | Error information from a failed [Command] | - -```js -// Success -{ "ok": 42 } - -// Failure -{ - "error": { - "dev/reason": "unauthorized", - "http/status": 401 - } -} -``` - -## Next Task(s) - -The result of an [Invocation] MAY include a request for further actions to be performed. This is a process of requesting that the invoker "enqueue" a new Task. This enables several things: a clean separation of pure return values from requesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. - -Enqueued [Task]s describe requests for future work to be performed. They SHOULD come with [Delegation]s, but MAY be a simple request back to the Invoker. - -All [Task]s in an [enqueue] array MUST be treated as concurrent, unless explicit data dependencies between them exist via [UCAN Promise]s. - -## Examples - -``` js -// DAG-JSON -{ - "s": {"/": {"bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU"}}, - "p": { - "h": {"/": {"bytes": "NBIFEgEAcQ"}}, - "ucan/r/1.0.0-rc.1": { - "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", - "ran": {"/": "bafkreictzcfwelyww7zmjkl5nptyot24oilky2bppw42nui2acozhfmzqa"}, - "out": {"ok": 42}, - "iat": 1702907627, - "prf": [{"/": "bafkreig3e34gbe65q4axx7bcsq3aei7urggprx7uynpltkhp4nclqzebbu"}], - } - } -} -``` - -# Proxy Execution - -If the Receipt Issuer is not identical to the `aud` field of Invocation referenced in the `ran` field, a [Delegation] proof chain SHOULD be included. If a chain is present, it MUST show that such a proxy execution was authorized by the original listed `aud` Agent. - -``` js -// Pseudocode - -const delegation = { - iss: "did:web:example.com", - aud: "did:plc:ewvi7nxzyoun6zhxrhs64oiz", - can: "crud/update", - // ... -} - -const workerDelegation = { - iss: "did:web:example.com", - aud: "did:web:worker.not-example.net", - sub: "did:web:example.com", - can: "ucan/execute", - // ... -} - -const invocation = { - iss: "did:plc:ewvi7nxzyoun6zhxrhs64oiz", - aud: "did:web:example.com", - prf: [ cid(delegation) ], - // ... -} - -const receipt = { - iss: "did:web:worker.not-example.net", - ran: cid(invocation) - prf: [ cid(workerDelegation) ], - // ... -} -``` - -## Examples - -``` js -// DAG-JSON -{ - "s": {"/": {"bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU"}}, - "p": { - "h": {"/": {"bytes": "NBIFEgEAcQ"}}, - "ucan/r/1.0.0-rc.1": { - "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", - "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", - "ran": {"/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu"}, - "out": {"ok": ["bob@example.com", "alice@example.com"]} - "prf": [] - } - } -} +] ``` # Prior Art +[Prior Art]: #prior-art [ucanto RPC] from [DAG House] is a production system that uses UCAN as the basis for an RPC layer. @@ -751,6 +501,7 @@ The Object Capability Network ([OCapN]) protocol extends [CapTP] with a generali [Cap 'n Proto RPC] is an influential RPC framework based on concepts from [CapTP]. # 7 Acknowledgements +[Acknowledgements]: #acknowledgements Many thanks to [Mark Miller] for his [trail blazing work][eRights] on [capability systems]. @@ -767,39 +518,7 @@ Thanks to [Philipp Krüger] for the enthusiastic feedback on the overall design Thanks to [Christine Lemmer-Webber] for the many conversations about capability systems and the programming models that they enable. Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the general IPLD worldview - - - -[^js-num]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined as a [IEEE-754] double-precision floating point number, which has a 53-bit significand. - - -[Action]: #action -[Arguments]: #arguments -[Audience]: #audience -[Cause]: #cause -[Command]: #command -[Execution Proxy]: #proxy-execution -[Executor]: #executor -[Expiry]: #expiry -[Invocation Payload]: #invocation-payload -[Invocation Signature]: #invocation-envelope -[Invocation]: #invocation-request -[Invoker]: #invoker -[Issued At]: #issued-at -[Issuer]: #issuer -[Metadata]: #metadata -[Nonce]: #nonce -[Proofs]: #proofs -[Receipt Payload]: #receipt-payload -[Receipt]: #receipt-response -[Response]: #response -[Result]: #result -[Subject]: #subject -[Task]: #task -[enqueue]: #enqueue -[lazy-vs-eager]: #lazy-vs-eager-evaluation - [Agoric]: https://agoric.com/ @@ -828,7 +547,9 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Mark Miller]: https://github.com/erights [OAuth 1]: https://oauth.net/1/ [OCapN]: https://github.com/ocapn/ +[Oxide Computer]: https://oxide.computer/ [Philipp Krüger]: https://github.com/matheus23/ +[Protocol Labs]: https://protocol.ai [Quinn Wilton]: https://github.com/QuinnWilton [Robust Composition]: http://www.erights.org/talks/thesis/markm-thesis.pdf [Rod Vagg]: https://github.com/rvagg/ @@ -837,6 +558,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [UCAN Delegation]: https://github.com/ucan-wg/delegation/ [UCAN Promise]: https://github.com/ucan-wg/promise/ [URI]: https://en.wikipedia.org/wiki/Uniform_Resource_Identifier +[Witchcraft Software]: https://github.com/expede [Zeeshan Lakhani]: https://github.com/zeeshanlakhani [`data`]: https://en.wikipedia.org/wiki/Data_URI_scheme [`ipfs`]: https://docs.ipfs.tech/how-to/address-ipfs-on-web/#native-urls @@ -845,4 +567,5 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [designation without authorization]: https://srl.cs.jhu.edu/pubs/SRL2003-02.pdf [distributed promise pipelines]: http://erights.org/elib/distrib/pipeline.html [eRights]: https://erights.org +[principle of least authority]: https://en.wikipedia.org/wiki/Principle_of_least_privilege [ucanto RPC]: https://github.com/web3-storage/ucanto From 37a76db0598f10aca9aa33432fb4a6554e980abb Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Aug 2024 17:50:06 -0700 Subject: [PATCH 117/127] Expand prf section --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 0f61f554..63595734 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ The executor is directed to perform some task described in the UCAN invocation b At a very high level: - A [Task] abstractly describes some Action to be run -- An [Invocation] attaches proven ([delegated][Delegation]) authority to a [Task], and requests it be run by a certain Agent +- An Invocation attaches proven ([delegated][Delegation]) authority to a [Task], and requests it be run by a certain Agent - A [Receipt] MAY request that the Invoker enqueue more [Task]s ``` mermaid @@ -163,7 +163,7 @@ erDiagram |--------------|-----------------------------------------------------------------------------| | [Command] | Function application; a description of work to be performed | | [Task] | Contextual information for a [Command], such as resource limits | -| [Invocation] | A request to perform some [Task] based on [delegated][Delegation] authority | +| Invocation | A request to perform some [Task] based on [delegated][Delegation] authority | A request for some work to be done (or to "exercise your authority") is an Invocation. @@ -190,7 +190,7 @@ flowchart TD cause -.->|CID| Receipt ``` -As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. The [Invocation] is a request to the [Executor] to perform the enclosed [Task]. [Invocation Payload]s are not executable until they have been signed and [Delegation] proofs validated. +As [noted in the introduction][Lazy vs Eager Evaluation], there is a difference between a reference to a function and calling that function. The Invocation is a request to the [Executor] to perform the enclosed [Task]. [Invocation Payload]s are not executable until they have been signed and [Delegation] proofs validated. Note that the Invocation MUST include the Signature envelope. An [Invocation Payload] on its own MUST NOT be considered a valid Invocation. @@ -303,10 +303,9 @@ The REQUIRED `nonce` field MUST include a random nonce. This field ensures that ### Proofs [Proofs]: #proofs -The `prf` field defines all [Delegation]s required to prove that this Invocation has an unbroken authorization chain. +The `prf` field lists the path of authority from the [Subject] to the [Invoker]. This MUST be an array of CIDs pointing [Delegations][Delegation] starting from the root Delegation (issued by the Subject), in strict sequence where the `aud` of the previous Delegation matches the `iss` of the next Delegation. -### Provenance -[Provenance]: #provenance +See [Proof Chains] for more detail #### Cause [Cause]: #cause @@ -404,7 +403,7 @@ flowchart RL { "h": {"/": {"bytes": "NBIFEgEAcQ"}}, "ucan/i/1.0.0-rc.1": { - "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", + "iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", "cmd": "/crud/create", "args": { @@ -426,9 +425,9 @@ flowchart RL }, "exp": 1697409438 "prf": [ - {"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}, - {"/": "bafkr4idnrqfouibxdqpvh2lmkhgsbw5yabvjbiaea3fplrb4vxifaphvgy"}, - {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"} + {"/": "zdpuAzx4sBrBCabrZZqXgvK3NDzh7Mf5mKbG11aBkkMCdLtCp"}, + {"/": "zdpuApTCXfoKh2sB1KaUaVSGofCBNPUnXoBb6WiCeitXEibZy"}, + {"/": "zdpuAoFdXRPw4n6TLcncoDhq1Mr6FGbpjAiEtqSBrTSaYMKkf"} ] } @@ -445,7 +444,7 @@ flowchart RL { "h": {"/": {"bytes": "NBIFEgEAcQ"}}, "ucan/i/1.0.0-rc.1": { - "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", + "iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", "cmd": "/msg/send", @@ -456,7 +455,7 @@ flowchart RL "body": "Let get coffee sometime and talk about UCAN Invocations!" }, "nonce": {"/": {"bytes": "TWFueSBopZ2h0IHdvcs"}}, - "prf": [{"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}], + "prf": [{"/": "zdpuAzx4sBrBCabrZZqXgvK3NDzh7Mf5mKbG11aBkkMCdLtCp"}], "exp": 1697409438 } } @@ -471,7 +470,7 @@ flowchart RL { "h": {"/": {"bytes": "NBIFEgEAcQ"}}, "ucan/i/1.0.0-rc.1": { - "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", + "iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", "aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", "meta": {"fuel": 999999}, @@ -556,6 +555,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Simon Worthington]: https://github.com/simonwo [Spritely Institute]: https://spritely.institute/news/introducing-a-distributed-debugger-for-goblins-with-time-travel.html [UCAN Delegation]: https://github.com/ucan-wg/delegation/ +[UCAN Envelope]: https://github.com/ucan-wg/spec/blob/main/README.md#envelope [UCAN Promise]: https://github.com/ucan-wg/promise/ [URI]: https://en.wikipedia.org/wiki/Uniform_Resource_Identifier [Witchcraft Software]: https://github.com/expede From e99387a1045a8649b7d4f880e1180a68a045c9f5 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Aug 2024 17:51:10 -0700 Subject: [PATCH 118/127] Add receipt link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 63595734..5090aad2 100644 --- a/README.md +++ b/README.md @@ -550,6 +550,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [Philipp Krüger]: https://github.com/matheus23/ [Protocol Labs]: https://protocol.ai [Quinn Wilton]: https://github.com/QuinnWilton +[Receipt]: https://github.com/ucan-wg/receipt [Robust Composition]: http://www.erights.org/talks/thesis/markm-thesis.pdf [Rod Vagg]: https://github.com/rvagg/ [Simon Worthington]: https://github.com/simonwo From 6736a3321b8ffc4be7a26cf935a6753a1d59aa34 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Aug 2024 17:53:42 -0700 Subject: [PATCH 119/127] Align exp field with high level spec --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 5090aad2..9172b70d 100644 --- a/README.md +++ b/README.md @@ -207,19 +207,19 @@ The UCAN envelope's payload tag MUST be `ucan/inv@1.0.0-rc.1`. The Invocation Payload attaches sender, receiver, and provenance to the [Task]. -| Field | Type | Required | Description | -|---------|--------------------|----------|--------------------------------------------------------------------| -| `iss` | `DID` | Yes | The DID of the [Invoker] | -| `sub` | `DID` | Yes | The [Subject] being invoked | -| `aud` | `DID` | No | The DID of the intended [Executor] if different from the [Subject] | -| `cmd` | `String` | Yes | The [Command] | -| `args` | `{String : Any}` | Yes | The [Command]'s [Arguments] | -| `prf` | `[&Delegation]` | Yes | [Delegation]s that prove the chain of authority | -| `meta` | `{String : Any}` | No | Arbitrary [Metadata] | -| `nonce` | `Bytes` | No | A unique, random nonce | -| `exp` | `Integer`[^js-num] | No | The timestamp at which the Invocation becomes invalid | -| `iat` | `Integer`[^js-num] | No | The timestamp at which the Invocation was created | -| `cause` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | +| Field | Type | Required | Description | +|---------|----------------------------|----------|--------------------------------------------------------------------| +| `iss` | `DID` | Yes | The DID of the [Invoker] | +| `sub` | `DID` | Yes | The [Subject] being invoked | +| `aud` | `DID` | No | The DID of the intended [Executor] if different from the [Subject] | +| `cmd` | `String` | Yes | The [Command] | +| `args` | `{String : Any}` | Yes | The [Command]'s [Arguments] | +| `prf` | `[&Delegation]` | Yes | [Delegation]s that prove the chain of authority | +| `meta` | `{String : Any}` | No | Arbitrary [Metadata] | +| `nonce` | `Bytes` | No | A unique, random nonce | +| `exp` | `Integer \| null`[^js-num] | Yes | The timestamp at which the Invocation becomes invalid | +| `iat` | `Integer`[^js-num] | No | The timestamp at which the Invocation was created | +| `cause` | `&Receipt` | No | An OPTIONAL CID of the [Receipt] that enqueued the [Task] | [^js-num]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined as a [IEEE-754] double-precision floating point number, which has a 53-bit significand. @@ -315,7 +315,7 @@ The OPTIONAL `cause` field is a provenance claim describing which [Receipt] requ #### Expiration [Expiration]: #expiration -The OPTIONAL field `exp` defines when the Invocation SHOULD time out. This is both expressive (defines a timeout, which is a best practice), and prevents replays. +The REQUIRED field `exp` defines when the Invocation SHOULD time out. This is both expressive (defines a timeout, which is a best practice), and prevents replays. #### Issued At [Issued At]: #issued-at From fd263af7b258b1cf4eb54ed004f75bac0e329ce2 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Aug 2024 17:56:20 -0700 Subject: [PATCH 120/127] Explain why exp is RECOMMENDED --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9172b70d..9776d5af 100644 --- a/README.md +++ b/README.md @@ -315,7 +315,7 @@ The OPTIONAL `cause` field is a provenance claim describing which [Receipt] requ #### Expiration [Expiration]: #expiration -The REQUIRED field `exp` defines when the Invocation SHOULD time out. This is both expressive (defines a timeout, which is a best practice), and prevents replays. +The REQUIRED nullable field `exp` defines when the Invocation SHOULD time out. Setting a timeout within a a few minutes is RECOMMENDED as it accounts for clock skew but limits the ability of an attacker to take advantage of an intercepted Invocation. In general, the smaller the time window the better. This is both expressive (defines a timeout, which is a best practice), and prevents replays. #### Issued At [Issued At]: #issued-at From 092311ffceff1fbc246c3f700856d022e02a9db7 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Aug 2024 18:00:16 -0700 Subject: [PATCH 121/127] Fix typos --- .github/workflows/words-to-ignore.txt | 1 + README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 1cad8012..88274e57 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -79,6 +79,7 @@ js-num memoization multiformat namespace +nullable outmost parallelize parameterizes diff --git a/README.md b/README.md index 9776d5af..46c8563d 100644 --- a/README.md +++ b/README.md @@ -254,7 +254,7 @@ The shape of the `args` MUST be defined by the `cmd` field type. This is similar #### Issuer [Issuer]: #issuer -The `iss` field MUST include the Issuer of the Invocation. This DID MUST match against the encoding signature. +The `iss` field MUST include the Issuer of the Invocation. This DID URL MUST dereference to the public key which proves the signature over the payload included in the envelope. #### Subject [Subject]: #subject From 6054476fbd8ac3adef50e684fa6d04126b2f9e9f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Aug 2024 18:03:38 -0700 Subject: [PATCH 122/127] Update ipldsch --- README.md | 3 ++- invocation.ipldsch | 8 +++----- receipt.ipldsch | 28 ---------------------------- signature.ipldsch | 9 --------- 4 files changed, 5 insertions(+), 43 deletions(-) delete mode 100644 receipt.ipldsch delete mode 100644 signature.ipldsch diff --git a/README.md b/README.md index 46c8563d..b9c72a3c 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,7 @@ Note that the Invocation MUST include the Signature envelope. An [Invocation Pay ## Type Tag [Type Tag]: #type-tag -The UCAN envelope's payload tag MUST be `ucan/inv@1.0.0-rc.1`. +The UCAN envelope's [payload tag] MUST be `ucan/inv@1.0.0-rc.1`. ## Invocation Payload [Invocation Payload]: #invocation-payload @@ -568,5 +568,6 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [designation without authorization]: https://srl.cs.jhu.edu/pubs/SRL2003-02.pdf [distributed promise pipelines]: http://erights.org/elib/distrib/pipeline.html [eRights]: https://erights.org +[payload tag]: https://github.com/ucan-wg/spec/blob/main/README.md#envelope [principle of least authority]: https://en.wikipedia.org/wiki/Principle_of_least_privilege [ucanto RPC]: https://github.com/web3-storage/ucanto diff --git a/invocation.ipldsch b/invocation.ipldsch index 6deda743..6cb44d6a 100644 --- a/invocation.ipldsch +++ b/invocation.ipldsch @@ -5,17 +5,15 @@ type Invocation struct { type SignaturePayload { h VarsigHeader - i Invocation + i InvocationV1Payload } -type InvocationPayload struct { - uiv SemVer - +type InvocationV1Payload struct { iss DID sub DID aud optional DID - do Command + cmd Command args {String : Any} nonce String diff --git a/receipt.ipldsch b/receipt.ipldsch deleted file mode 100644 index 27c5f539..00000000 --- a/receipt.ipldsch +++ /dev/null @@ -1,28 +0,0 @@ -type Receipt { - s Signature - p SignaturePayload -} - -type SignaturePayload { - h VarsigHeader - r ReceiptPayload -} - -type ReceiptPayload struct { - iss DID - ran &Invocation - - enq [&Task] - out Result - - iat Integer - mta {String : Any} - - prf [&Delegation] - rec &Receipt -} - -type Result union { - | any "ok" - | any "error" -} representation keyed diff --git a/signature.ipldsch b/signature.ipldsch deleted file mode 100644 index fc4e7118..00000000 --- a/signature.ipldsch +++ /dev/null @@ -1,9 +0,0 @@ -type Signature union { - | inline Bytes - | batch &BatchSignature -} - -type BatchSignature struct { - scp [&Any] - sig Bytes -} From ba2c11125b2170131c70fa967df23976e2faee7b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Aug 2024 18:04:44 -0700 Subject: [PATCH 123/127] Add to dictionary --- .github/workflows/words-to-ignore.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 88274e57..fc312821 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -66,6 +66,7 @@ cryptographically dataflow de delegator +dereference dholms dispatchable enqueuing From 370a68d0e119e438e314b86aafd2b61ff8d24ad7 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Aug 2024 18:05:21 -0700 Subject: [PATCH 124/127] Remove dead link --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index b9c72a3c..7d1a762d 100644 --- a/README.md +++ b/README.md @@ -532,7 +532,6 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [DAG-CBOR]: https://ipld.io/specs/codecs/dag-cbor/spec/ [DID]: https://www.w3.org/TR/did-core/ [Delegation]: https://github.com/ucan-wg/delegation -[E-lang Mailing List, 2000 Oct 18]: http://wiki.erights.org/wiki/Capability-based_Active_Invocation_Certificates [Electronic Rights Transfer Protocol (ERTP)]: https://docs.agoric.com/guides/ertp/ [Fission]: https://fission.codes/ [Haskell]: https://en.wikipedia.org/wiki/Haskell From b5732eb3c491e7cf3a0f61d95f639397fa09ee84 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Aug 2024 18:10:09 -0700 Subject: [PATCH 125/127] Clarify per Juan --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7d1a762d..6d9d92e4 100644 --- a/README.md +++ b/README.md @@ -274,7 +274,7 @@ The OPTIONAL `aud` field specified the intended recipient of Invocation, otherwi A Task is the subset of Invocation fields that uniquely determine the work to be performed. The nonce is important for distinguishing between non-idempotent executions of a Task by making the group together unique. -A Task MUST be uniquely defined by a Task ID that is the CID of the following fields: +A Task MUST be uniquely defined by a Task ID that is the CID of the following fields as a keyed map: - [Subject] - [Command] @@ -293,7 +293,7 @@ The REQUIRED Command (`cmd`) field MUST contain a concrete, dispatchable message The REQUIRED Arguments (`args`) field, MAY contain any parameters expected by the Command. The Subject MUST be considered the authority on the shape of this data. This field MUST be representable as a map or keyword list. -The Arguments MUST pass validation of the Policies on all of the [UCAN Delegations][UCAN Delegation] in the [Proofs] field. If any Policy reports failure, the Invocation MUST be rejected. +The Arguments MUST pass validation of the Policies on all of the [UCAN Delegations][UCAN Delegation] in the [Proofs] field. If any [Policy] reports failure against the Invocation's Arguments, the Invocation MUST be rejected. #### Nonce [Nonce]: #nonce @@ -547,6 +547,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [OCapN]: https://github.com/ocapn/ [Oxide Computer]: https://oxide.computer/ [Philipp Krüger]: https://github.com/matheus23/ +[Policy]: https://github.com/ucan-wg/delegation#policy [Protocol Labs]: https://protocol.ai [Quinn Wilton]: https://github.com/QuinnWilton [Receipt]: https://github.com/ucan-wg/receipt From 60caf3fc8824ba19369706d87935b2f94f1713df Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Aug 2024 18:11:32 -0700 Subject: [PATCH 126/127] Thanks Juan C! --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6d9d92e4..a1afbb60 100644 --- a/README.md +++ b/README.md @@ -517,6 +517,8 @@ Thanks to [Philipp Krüger] for the enthusiastic feedback on the overall design Thanks to [Christine Lemmer-Webber] for the many conversations about capability systems and the programming models that they enable. Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the general IPLD worldview + +Many thanks to [Juan Caballero] for his detailed questions and comments to help polish the spec. @@ -540,6 +542,7 @@ Thanks to [Rod Vagg] for the clarifications on IPLD Schema implicits and the gen [IPVM]: https://github.com/ipvm-wg [Irakli Gozalishvili]: https://github.com/Gozala [JS Number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number +[Juan Caballero]: https://github.com/bumblefudge [Luke Marsen]: https://github.com/lukemarsden [Marc-Antoine Parent]: https://github.com/maparent [Mark Miller]: https://github.com/erights From a99566fb9d5c62aae241d49a599a7f4454a8cd5d Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Aug 2024 18:13:08 -0700 Subject: [PATCH 127/127] without -> with --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a1afbb60..39f644db 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ However, there is clearly a distinction between passing a function and invoking ## Public Resources [Public Resources]: #public-resources -A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard requirement for initiating a flow (e.g. sign up). These cases typically involve a reference passed out of band (such as a web link). Due to [designation without authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain, but this SHOULD NOT be the default behavior. +A core part of UCAN's design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard requirement for initiating a flow (e.g. sign up). These cases typically involve a reference passed out of band (such as a web link). Due to [designation with authorization], knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a "closed-loop" proof chain, but this SHOULD NOT be the default behavior. ## Promise Pipelining [Promise Pipelining]: #promise-pipelining @@ -568,7 +568,7 @@ Many thanks to [Juan Caballero] for his detailed questions and comments to help [`ipfs`]: https://docs.ipfs.tech/how-to/address-ipfs-on-web/#native-urls [`magnet`]: https://en.wikipedia.org/wiki/Magnet_URI_scheme [capability systems]: https://en.wikipedia.org/wiki/Capability-based_security -[designation without authorization]: https://srl.cs.jhu.edu/pubs/SRL2003-02.pdf +[designation with authorization]: https://srl.cs.jhu.edu/pubs/SRL2003-02.pdf [distributed promise pipelines]: http://erights.org/elib/distrib/pipeline.html [eRights]: https://erights.org [payload tag]: https://github.com/ucan-wg/spec/blob/main/README.md#envelope