From 02fc52e43a8ecba00a89072ae04dd8d8d9643f36 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 15 Aug 2022 23:50:44 -0700 Subject: [PATCH 001/154] Rough notes --- README.md | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c1b85903..784c3388 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,52 @@ * _ -# 0. Abstract +# 0 Abstract ## 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). +# 1 Rough Proposal + +THIS DOCUMENT IS CURRENTLY A VERY ROUGH PROPOSAL, USED FOR DESIGN COMMUNICATION WITH COLLABORATORS ONLY + +THIS IS HEAVILY PRE-ALPHA. DO NOT USE THIS. + + +UCAN uses a capabilities model. The + +* UCANTO +* zcap + + +https://noti.st/expede/oq0ULd/ipvm-interplanetary-vm#smD6TZT + +## Invocation Format + +``` + ┌───────────┐ + │ │ + │ Receipt │ + │ │ + └┬─────────┬┘ + │ │ + │ │ + │ │ + ┌────────────────▼┐ ┌─▼──────────┐ + │ │ │ │ + │ Invocation │ │ Result │ + │ │ │ │ + └──────┬────┬─────┘ └────────────┘ + │ │ + │ │ + │ │ +┌─────────▼┐ ┌▼───────────┐ +│ │ │ │ +│ UCAN │ │ Scheduling │ +│ │ │ │ +└──────────┘ └────────────┘ +``` + +## Receipts + From f3c64fcb1b6269237b6ca909b867c144474b9110 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 25 Nov 2022 17:28:48 -0800 Subject: [PATCH 002/154] Add authors, start sketching out the basics --- README.md | 66 +++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 784c3388..07f40e9c 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ -# UCAN Invocation +# UCAN Invocation Specification v0.1.0 ## Editors -* _ +* [Brooklyn Zelenka](https://github.com/expede/), [Fission](https://fission.codes/) ## Authors -* _ +* [Brooklyn Zelenka](https://github.com/expede/), [Fission](https://fission.codes/) +* [Irakli Gozalishvili](https://github.com/Gozala), [DAG House](https://dag.house/) # 0 Abstract @@ -14,46 +15,43 @@ 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). -# 1 Rough Proposal +# 1 Introduction -THIS DOCUMENT IS CURRENTLY A VERY ROUGH PROPOSAL, USED FOR DESIGN COMMUNICATION WITH COLLABORATORS ONLY +## Separation -THIS IS HEAVILY PRE-ALPHA. DO NOT USE THIS. +Why separate the UCAN from the invocation format? Surely the UCAN itself already contains all the infomation required. +I would argue that this is not the case for a few reasons. -UCAN uses a capabilities model. The +1. Authority is not intent to execute. Authority transfer is first-class in UCAN, other actions are not +2. Mixing the two levels means that invocation info will live with the token (as a proof) on subdelegations +3. Other detail, such as scheduling -* UCANTO -* zcap +A job request may be picked up by anyone, PRIOR to UCAN delegation. A handshake or matchmaking may need to be performed. +## Authority Is Not Intention -https://noti.st/expede/oq0ULd/ipvm-interplanetary-vm#smD6TZT +Or to put it another way: "just because you can, doens't mean you should". Granting a UCAN to a peer means that they are allowed to perform some actions for a period of time. This is a feature not a bug, but also says nothing about the intention of when it should be run. I may grant a collaborative process the ability to perform actions on an ongoing basis (hmm, but vioating POLA) -## Invocation Format +I could preload the service with `n` UCANs with narrow time windows and `nbf`s in the future. That would certainly be very secure, but it would be less convenient since I need to come online to issue more or to -``` - ┌───────────┐ - │ │ - │ Receipt │ - │ │ - └┬─────────┬┘ - │ │ - │ │ - │ │ - ┌────────────────▼┐ ┌─▼──────────┐ - │ │ │ │ - │ Invocation │ │ Result │ - │ │ │ │ - └──────┬────┬─────┘ └────────────┘ - │ │ - │ │ - │ │ -┌─────────▼┐ ┌▼───────────┐ -│ │ │ │ -│ UCAN │ │ Scheduling │ -│ │ │ │ -└──────────┘ └────────────┘ +# 2 Envelope + +UCAN uses a capabilities model. The + +* UCANTO +* zcap +* IPVM + + ``` json +{ + "ucan/invoke": [ "bafyLeft", "bafyRight", "bafyEnd" ] + "version": "0.1.0", + /* "nonce": "abcdef" -- I think the nonce inside each UCAN is sufficent? */ + "siganture": 0xCOFFEE +} ``` -## Receipts +# 3 UCAN Pipelining +At time of creation, a UCAN MAY not know the concrete value required to scope the resource down sufficiently. This MAY be caused either by invoking them both in the same payload, or following one after another by CID reference. From cadd78cf22fee90e07a98ec53443b9321bdb6b80 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 25 Nov 2022 17:36:18 -0800 Subject: [PATCH 003/154] Configure GH actions --- .github/workflows/spellcheck.yml | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index 08121975..06710ca3 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -1,18 +1,12 @@ -name: 'spellcheck' -on: - pull_request: +name: Spellcheck Action +on: push jobs: - spellcheck: + build: + name: Spellcheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: matheus23/md-spellcheck-action@v4.1.0 - with: - files-to-check: "**/*.md" - files-to-exclude: | - CODE_OF_CONDUCT.md - CONTRIBUTING.md - Community_Specification_License-v1.md - Notices.md - words-to-ignore-file: ./.github/workflows/words-to-ignore.txt + # The checkout step + - uses: actions/checkout@master + - uses: rojopolis/spellcheck-github-actions@0.24.0 + name: Spellcheck From 29c014ca6e5c61ac92f5816e008ce0cb3f4a2487 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 25 Nov 2022 17:38:52 -0800 Subject: [PATCH 004/154] Update README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 07f40e9c..380f309e 100644 --- a/README.md +++ b/README.md @@ -55,3 +55,5 @@ UCAN uses a capabilities model. The # 3 UCAN Pipelining At time of creation, a UCAN MAY not know the concrete value required to scope the resource down sufficiently. This MAY be caused either by invoking them both in the same payload, or following one after another by CID reference. + +Relative From a251084d4cf8b68ed73192873b4902b8896f39a9 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 25 Nov 2022 18:57:41 -0800 Subject: [PATCH 005/154] Start on receipts --- README.md | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 380f309e..2a9d6bf0 100644 --- a/README.md +++ b/README.md @@ -47,13 +47,56 @@ UCAN uses a capabilities model. The { "ucan/invoke": [ "bafyLeft", "bafyRight", "bafyEnd" ] "version": "0.1.0", - /* "nonce": "abcdef" -- I think the nonce inside each UCAN is sufficent? */ + /* "nonce": "abcdef" -- I think the nonce inside each UCAN is sufficent? But also can never hurt */ "siganture": 0xCOFFEE } ``` -# 3 UCAN Pipelining +# 3 Receipt & Attestation + +An invocation receipt is a claim about what the output of an invocation is. A receipt MUST be attested via signature of the principal (the audience of the associated invocation). + +Note that this does not guarantee correctness of the result! The level of this statement's veracity MUST be ony taken that the signer claims this to be a fact. + +``` json +{ + "ucan/invocation/receipt": { + "bafyLeft": { + "a": 42, + "example.com": { + "msg/read": [ + "from": "alice@example.com", + "text": "hello world" + ] + } + }, + "bafyRight": { + "sub.example.com?TYPE=TXT": { + "crud/update": { + "12345": { + "http": { + "status": 200 + }, + "value": "lorem ipsum" + } + } + } + } + }, + "version": "0.1.0", + "nonce": "xyz", + "signature": 0xB00 +} +``` + +# 4 Pipelining + +> 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 +> +> Robust Composition, Mark Miller At time of creation, a UCAN MAY not know the concrete value required to scope the resource down sufficiently. This MAY be caused either by invoking them both in the same payload, or following one after another by CID reference. -Relative +Variables relative to the result of some other action MAY be used. In this case, the attested (signed) receipt of the previous action MUST be included in the following form: + +Refeernced by invocation CID From e57a7ac9fb51362f8d89a2654953ab5370aa44d6 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 25 Nov 2022 19:31:01 -0800 Subject: [PATCH 006/154] IPLD basics --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index 2a9d6bf0..191c4869 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ * [Brooklyn Zelenka](https://github.com/expede/), [Fission](https://fission.codes/) * [Irakli Gozalishvili](https://github.com/Gozala), [DAG House](https://dag.house/) +## Depends On + +* [`ucan-ipld`](https://github.com/ucan-wg/ucan-ipld/) + # 0 Abstract ## Language @@ -58,6 +62,30 @@ An invocation receipt is a claim about what the output of an invocation is. A re Note that this does not guarantee correctness of the result! The level of this statement's veracity MUST be ony taken that the signer claims this to be a fact. +The receipt MUST be signed with by the `aud` from the UCAN. + +## 3.1 IPLD + +```ipldsch +type Receipt struct { + i {&UCAN : {URI : Any}} + v SemVer + n String + s Bytes +} + +type SemVer struct { + ma Integer + mi Integer + pa Integer + ta optional nullable String +} + +type URI = String +``` + +## 3.2 JSON Example + ``` json { "ucan/invocation/receipt": { From 68a387e4dacc6491cd908b7b29ecfd76e941fb65 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 25 Nov 2022 19:42:16 -0800 Subject: [PATCH 007/154] More IPLD --- README.md | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 191c4869..85755045 100644 --- a/README.md +++ b/README.md @@ -47,11 +47,31 @@ UCAN uses a capabilities model. The * zcap * IPVM +## 2.1 IPLD + +``` ipldsch +type Invocation struct { + i [&UCAN] + v SemVer + n String + s Bytes +} + +type SemVer struct { + ma Integer + mi Integer + pa Integer + ta optional nullable String +} +``` + +## 2.2 Exmaple + ``` json { "ucan/invoke": [ "bafyLeft", "bafyRight", "bafyEnd" ] "version": "0.1.0", - /* "nonce": "abcdef" -- I think the nonce inside each UCAN is sufficent? But also can never hurt */ + "nonce": "abcdef", "siganture": 0xCOFFEE } ``` @@ -66,7 +86,7 @@ The receipt MUST be signed with by the `aud` from the UCAN. ## 3.1 IPLD -```ipldsch +``` ipldsch type Receipt struct { i {&UCAN : {URI : Any}} v SemVer @@ -74,13 +94,6 @@ type Receipt struct { s Bytes } -type SemVer struct { - ma Integer - mi Integer - pa Integer - ta optional nullable String -} - type URI = String ``` From 16956fb9fb2eb7483338f2146f61e92d46c32a2b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 25 Nov 2022 20:36:41 -0800 Subject: [PATCH 008/154] I don't totally love the current format, but unclear how to get IPLD to look like this --- README.md | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 85755045..bcbbd6d0 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,14 @@ UCAN uses a capabilities model. The * zcap * IPVM + + + +Specify Roles + + + + ## 2.1 IPLD ``` ipldsch @@ -101,7 +109,7 @@ type URI = String ``` json { - "ucan/invocation/receipt": { + "ucan/receipt": { "bafyLeft": { "a": 42, "example.com": { @@ -141,3 +149,40 @@ At time of creation, a UCAN MAY not know the concrete value required to scope th Variables relative to the result of some other action MAY be used. In this case, the attested (signed) receipt of the previous action MUST be included in the following form: Refeernced by invocation CID + + +### 4.1 IPLD Schema + +``` ipldsch +type RelativeOutput struct { + i &Invocation + o Path -- output path +} + +type Path String +``` + +### 4.2 JSON Example + +``` json +{ + "ucan/invoked": "bafy12345", + "output": "example/a/b" +} +``` + +Inside a next UCAN, substitution of a previous unresolved step MUST be represented as: + +``` js +{ + // ..., + "att": { + "example.com": { + "path": { + "ucan/invoked": "bafy12345", + "output": "example.com/a/b" + } + } + } +} +``` From 428378a98ed57c3a9e0a0f7480f4a255a3c86f93 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 25 Nov 2022 22:05:38 -0800 Subject: [PATCH 009/154] WIP ahead of Irakli --- README.md | 48 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index bcbbd6d0..de2c93d5 100644 --- a/README.md +++ b/README.md @@ -59,10 +59,10 @@ Specify Roles ``` ipldsch type Invocation struct { - i [&UCAN] - v SemVer - n String - s Bytes + inv [&UCAN] -- To invoke + v SemVer -- Version + nnc String -- Nonce + sig Bytes -- Signature } type SemVer struct { @@ -78,9 +78,9 @@ type SemVer struct { ``` json { "ucan/invoke": [ "bafyLeft", "bafyRight", "bafyEnd" ] - "version": "0.1.0", - "nonce": "abcdef", - "siganture": 0xCOFFEE + "v": "0.1.0", + "nnc": "abcdef", + "sig": 0xCOFFEE } ``` @@ -96,10 +96,11 @@ The receipt MUST be signed with by the `aud` from the UCAN. ``` ipldsch type Receipt struct { - i {&UCAN : {URI : Any}} - v SemVer - n String - s Bytes + inv {&UCAN : {URI : Any}} + v SemVer + nnc String + ext optional {String : Any} + sig Bytes } type URI = String @@ -132,9 +133,12 @@ type URI = String } } }, - "version": "0.1.0", - "nonce": "xyz", - "signature": 0xB00 + "v": "0.1.0", + "nnc": "xyz", + "ext": { + "notes": "wow, what a " + }, + "sig": 0xB00 } ``` @@ -164,11 +168,17 @@ type Path String ### 4.2 JSON Example +Some alternates: + ``` json { "ucan/invoked": "bafy12345", "output": "example/a/b" } + +"ucan:out:bafy12345/example/a/b" + + ``` Inside a next UCAN, substitution of a previous unresolved step MUST be represented as: @@ -177,8 +187,8 @@ Inside a next UCAN, substitution of a previous unresolved step MUST be represent { // ..., "att": { - "example.com": { - "path": { + "example.com/$PATH": { + "$PATH": { "ucan/invoked": "bafy12345", "output": "example.com/a/b" } @@ -186,3 +196,9 @@ Inside a next UCAN, substitution of a previous unresolved step MUST be represent } } ``` + +NOTE security, becase the aud controls the receipt of the first part of the pipeline, they control anything under the example.com namespace + + + +FIXME add diagram of required invocation -> receipt signers From 99679aaa93af9683d58ee62436919e33d6877d1b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 10:57:08 -0800 Subject: [PATCH 010/154] WIP fleshig out wrapper --- README.md | 114 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 86 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index de2c93d5..c8a31a78 100644 --- a/README.md +++ b/README.md @@ -21,66 +21,105 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S # 1 Introduction -## Separation +## 1.1 Motivation Why separate the UCAN from the invocation format? Surely the UCAN itself already contains all the infomation required. +A UCAN contains everythiung needed to execute a request + +"intention" + +Dleegation and execution are opposite pointing arrows + +## 1.2 Separation + I would argue that this is not the case for a few reasons. 1. Authority is not intent to execute. Authority transfer is first-class in UCAN, other actions are not 2. Mixing the two levels means that invocation info will live with the token (as a proof) on subdelegations 3. Other detail, such as scheduling -A job request may be picked up by anyone, PRIOR to UCAN delegation. A handshake or matchmaking may need to be performed. - -## Authority Is Not Intention +## 1.3 Authority Is Not Intention -Or to put it another way: "just because you can, doens't mean you should". Granting a UCAN to a peer means that they are allowed to perform some actions for a period of time. This is a feature not a bug, but also says nothing about the intention of when it should be run. I may grant a collaborative process the ability to perform actions on an ongoing basis (hmm, but vioating POLA) +"Just because you can, doens't mean you should". Granting a UCAN to a peer means that they are allowed to perform some actions for a period of time. This is a feature not a bug, but also says nothing about the intention of when it should be run. I may grant a collaborative process the ability to perform actions on an ongoing basis (hmm, but vioating POLA) I could preload the service with `n` UCANs with narrow time windows and `nbf`s in the future. That would certainly be very secure, but it would be less convenient since I need to come online to issue more or to -# 2 Envelope +# 2 Roles + +Invocation adds two new roles to UCAN: invoker and executor. The existing UCAN delegator and delgate pricipals MUST persist to the invocation. + +| UCAN Field | Delegation | Invocation | +|------------|-----------------------------|-------------------------| +| `iss` | Transfer authority (active) | Request action (active) | +| `aud` | Receive authority (passive) | Perform action (active) | + +## 2.1 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.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 Envelope + +The invocation envelope is a thin wrapper around a UCAN that conveys that all of the contained UCAN actions SHOULD be performed. + +All of the roles from the referenced UCANs MUST persist to the invocation per the [above table](FIXME). + +The invocation wrapper MUST be signed by the same principal that issued the UCAN. + +## 3.1 Fields -UCAN uses a capabilities model. The +| Field | Type | Value | Descrption | Required | +|---------------|----------|-----------|--------------------------------------------------|----------| +| `ucan/invoke` | `CID` | | The CID of the UCAN to invoke | Yes | +| `v` | `SemVer` | `"0.1.0"` | SemVer of the UCAN invocation object schema | Yes | +| `nnc` | `String` | | A unique nonce, to distinguish each invocation | Yes | +| `sig` | `Bytes` | | Signature of the rest of the field canonicalized | Yes | + +### 3.1.1 Invocation -* UCANTO -* zcap -* IPVM +The `ucan/invoke` field MUST contain CIDs pointing to the UCANs to invoke. The outmost UCAN being invoked MUST NOT contain any actions that are not intended to be executed. +### 3.1.2 Version +The `v` field MUST contain the version of the invocation object schema. +### 3.1.3 Nonce -Specify Roles +The `nnc` field MUS contain a unique nonce. This field makes each invocation unique. +### 3.1.4 Signature +The `sig` field MUST contain the signature of the other fields. To construct the payload, the other fields MUST first be canonicalized as `dag-cbor`. +If serialzied as JSON, the `sig` field MUST be serialized as [unpadded base64url](https://datatracker.ietf.org/doc/html/rfc4648#section-5). -## 2.1 IPLD +## 3.2 IPLD Schema ``` ipldsch type Invocation struct { - inv [&UCAN] -- To invoke - v SemVer -- Version - nnc String -- Nonce - sig Bytes -- Signature -} - -type SemVer struct { - ma Integer - mi Integer - pa Integer - ta optional nullable String + inv &UCAN -- To invoke + v SemVer -- Version + nnc String -- Nonce + sig Bytes -- Signature } ``` -## 2.2 Exmaple +## 3.3 JSON Exmaple ``` json { - "ucan/invoke": [ "bafyLeft", "bafyRight", "bafyEnd" ] + "ucan/invoke": "bafyUCAN", "v": "0.1.0", "nnc": "abcdef", - "sig": 0xCOFFEE + "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT+SRK6v/SX8bjt+VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" } ``` @@ -142,7 +181,7 @@ type URI = String } ``` -# 4 Pipelining +# 4 Promise Pipelining > 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 > @@ -200,5 +239,24 @@ Inside a next UCAN, substitution of a previous unresolved step MUST be represent NOTE security, becase the aud controls the receipt of the first part of the pipeline, they control anything under the example.com namespace +# 5 Appendix + +## 5.1 Support Types + +``` ipldsch +type SemVer struct { + num NumVer + tag nullable optional String +} representation stringjoin { + join "+" +} + +type NumVer struct { + ma Integer + mi Integer + pa Integer +} representation stringjoin { + join "." +} +``` -FIXME add diagram of required invocation -> receipt signers From d9b0fee99431d137ec80ebc220765404b052b0f6 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 11:11:25 -0800 Subject: [PATCH 011/154] Early thoughts on delegation vs invocation text --- README.md | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c8a31a78..b9636258 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,16 @@ The executor MUST be the UCAN delegate. Their DID MUST be set the in `aud` field # 3 Envelope + + The invocation envelope is a thin wrapper around a UCAN that conveys that all of the contained UCAN actions SHOULD be performed. All of the roles from the referenced UCANs MUST persist to the invocation per the [above table](FIXME). @@ -76,12 +86,13 @@ The invocation wrapper MUST be signed by the same principal that issued the UCAN ## 3.1 Fields -| Field | Type | Value | Descrption | Required | -|---------------|----------|-----------|--------------------------------------------------|----------| -| `ucan/invoke` | `CID` | | The CID of the UCAN to invoke | Yes | -| `v` | `SemVer` | `"0.1.0"` | SemVer of the UCAN invocation object schema | Yes | -| `nnc` | `String` | | A unique nonce, to distinguish each invocation | Yes | -| `sig` | `Bytes` | | Signature of the rest of the field canonicalized | Yes | +| Field | Type | Value | Descrption | Required | Default | +|---------------|----------|-----------|--------------------------------------------------|----------|---------| +| `ucan/invoke` | `CID` | | The CID of the UCAN to invoke | Yes | | +| `v` | `SemVer` | `"0.1.0"` | SemVer of the UCAN invocation object schema | Yes | | +| `nnc` | `String` | | A unique nonce, to distinguish each invocation | Yes | | +| `ext` | `Any` | | Nonnormative extended fields | No | `null` | +| `sig` | `Bytes` | | Signature of the rest of the field canonicalized | Yes | | ### 3.1.1 Invocation @@ -90,12 +101,16 @@ The `ucan/invoke` field MUST contain CIDs pointing to the UCANs to invoke. The o ### 3.1.2 Version The `v` field MUST contain the version of the invocation object schema. - + ### 3.1.3 Nonce The `nnc` field MUS contain a unique nonce. This field makes each invocation unique. -### 3.1.4 Signature +### 3.1.4 Extended Fields + +The OPTIONAL `ext` field MAY contain arbitrary data. If not present, the `ext` field MUST me interpreted as `null`, including for [signature](#315-signature). + +### 3.1.5 Signature The `sig` field MUST contain the signature of the other fields. To construct the payload, the other fields MUST first be canonicalized as `dag-cbor`. @@ -108,6 +123,7 @@ type Invocation struct { inv &UCAN -- To invoke v SemVer -- Version nnc String -- Nonce + ext Any -- Extended fields sig Bytes -- Signature } ``` @@ -119,17 +135,18 @@ type Invocation struct { "ucan/invoke": "bafyUCAN", "v": "0.1.0", "nnc": "abcdef", + "ext": null, "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT+SRK6v/SX8bjt+VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" } ``` # 3 Receipt & Attestation -An invocation receipt is a claim about what the output of an invocation is. A receipt MUST be attested via signature of the principal (the audience of the associated invocation). +An invocation receipt is a claim about what the output of an invocation. A receipt MUST be attested via signature of the principal (the audience of the associated invocation). Note that this does not guarantee correctness of the result! The level of this statement's veracity MUST be ony taken that the signer claims this to be a fact. -The receipt MUST be signed with by the `aud` from the UCAN. +The receipt MUST be signed by the `aud` from the UCAN. ## 3.1 IPLD From c1bc821bef84de59a830f7273dbc5de9c6df505f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 12:20:20 -0800 Subject: [PATCH 012/154] Add fields for pipelining --- README.md | 103 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index b9636258..51f90c31 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ ## Depends On +* [UCAN](https://github.com/ucan-wg/spec/) >=v0.10 * [`ucan-ipld`](https://github.com/ucan-wg/ucan-ipld/) # 0 Abstract @@ -86,13 +87,14 @@ The invocation wrapper MUST be signed by the same principal that issued the UCAN ## 3.1 Fields -| Field | Type | Value | Descrption | Required | Default | -|---------------|----------|-----------|--------------------------------------------------|----------|---------| -| `ucan/invoke` | `CID` | | The CID of the UCAN to invoke | Yes | | -| `v` | `SemVer` | `"0.1.0"` | SemVer of the UCAN invocation object schema | Yes | | -| `nnc` | `String` | | A unique nonce, to distinguish each invocation | Yes | | -| `ext` | `Any` | | Nonnormative extended fields | No | `null` | -| `sig` | `Bytes` | | Signature of the rest of the field canonicalized | Yes | | +| Field | Type | Value | Descrption | Required | Default | +|---------------|------------------------------------|-----------|--------------------------------------------------|----------|---------| +| `ucan/invoke` | `CID` | | The CID of the UCAN to invoke | Yes | | +| `v` | `SemVer` | `"0.1.0"` | SemVer of the UCAN invocation object schema | Yes | | +| `run` | `"*" or {URI : {Ability : [Any]}}` | | Which UCAN capabilities to run | No | `"*"` | +| `nnc` | `String` | | A unique nonce, to distinguish each invocation | Yes | | +| `ext` | `Any` | | Nonnormative extended fields | No | `null` | +| `sig` | `Bytes` | | Signature of the rest of the field canonicalized | Yes | | ### 3.1.1 Invocation @@ -101,16 +103,20 @@ The `ucan/invoke` field MUST contain CIDs pointing to the UCANs to invoke. The o ### 3.1.2 Version The `v` field MUST contain the version of the invocation object schema. - -### 3.1.3 Nonce + +### 3.1.3 Capabilities + +The OPTIONAL `run` field specifies which actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](FIXME)) are intended to be run, then they MUST be treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or lesser scope. + +### 3.1.4 Nonce The `nnc` field MUS contain a unique nonce. This field makes each invocation unique. -### 3.1.4 Extended Fields +### 3.1.5 Extended Fields The OPTIONAL `ext` field MAY contain arbitrary data. If not present, the `ext` field MUST me interpreted as `null`, including for [signature](#315-signature). -### 3.1.5 Signature +### 3.1.6 Signature The `sig` field MUST contain the signature of the other fields. To construct the payload, the other fields MUST first be canonicalized as `dag-cbor`. @@ -120,12 +126,18 @@ If serialzied as JSON, the `sig` field MUST be serialized as [unpadded base64url ``` ipldsch type Invocation struct { - inv &UCAN -- To invoke + inv &UCAN -- The UCAN providing authority v SemVer -- Version + run Scope -- Which actions to invoke nnc String -- Nonce ext Any -- Extended fields sig Bytes -- Signature } + +type Scope enum { + | All ("*") + | {URI : {Ability : [Any]}} +} ``` ## 3.3 JSON Exmaple @@ -140,35 +152,40 @@ type Invocation struct { } ``` -# 3 Receipt & Attestation +# 4 Receipt + +An invocation receipt is an attested result about the output of an invocation. A receipt MUST be attested via signature of the principal (the `aud` of the associated UCAN). -An invocation receipt is a claim about what the output of an invocation. A receipt MUST be attested via signature of the principal (the audience of the associated invocation). +Note that this does not guarantee correctness of the result! The statement's veracity MUST be only understood as an attestation from the executor. -Note that this does not guarantee correctness of the result! The level of this statement's veracity MUST be ony taken that the signer claims this to be a fact. +## 4.1 Fields -The receipt MUST be signed by the `aud` from the UCAN. +| Field | Type | Value | Descrption | Required | Default | +|----------------|---------------------------|-----------|--------------------------------------------------|----------|---------| +| `ucan/receipt` | `{CID => {"URI" => Any}}` | | The CID of the UCAN to invoke | Yes | | +| `v` | `SemVer` | `"0.1.0"` | SemVer of the UCAN invocation object schema | Yes | | +| `nnc` | `String` | | A unique nonce, to distinguish each receipt | Yes | | +| `ext` | `Any` | | Nonnormative extended fields | No | `null` | +| `sig` | `Bytes` | | Signature of the rest of the field canonicalized | Yes | | -## 3.1 IPLD +## 4.1 IPLD ``` ipldsch type Receipt struct { - inv {&UCAN : {URI : Any}} + rec {CID : {URI : {Ability : {String : Any}}} v SemVer nnc String - ext optional {String : Any} + ext optional Any sig Bytes -} - -type URI = String +} ``` -## 3.2 JSON Example +## 4.2 JSON Example ``` json { "ucan/receipt": { "bafyLeft": { - "a": 42, "example.com": { "msg/read": [ "from": "alice@example.com", @@ -192,26 +209,42 @@ type URI = String "v": "0.1.0", "nnc": "xyz", "ext": { - "notes": "wow, what a " + "notes": "very receipt. such wow.", + "tags": ["dag-house", "fission"] }, "sig": 0xB00 } ``` -# 4 Promise Pipelining +# 5 Promise Pipelining > 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 > -> Robust Composition, Mark Miller +> Mark Miller, Robust Composition + +At UCAN creation time, the UCAN MAY not yet have all of the information required to construct the next request in a sequence. Waiting for each request to complete before proceeding to the next action 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 earlier invocations into the input of a later one. This liberates the invoker from waiting for each step. + +The authority to execute later actions often cannot be fully attenuated in advance, since the executor controls the reported output of the prior step in a pipeline. When chosing to use pipelining, the invoker MUST delegate capabilities for any of the possible outputs. If tight control is required to limit authority, pipelining SHOULD NOT be used. + -At time of creation, a UCAN MAY not know the concrete value required to scope the resource down sufficiently. This MAY be caused either by invoking them both in the same payload, or following one after another by CID reference. + + + + + + +the know the concrete value required to scope the resource down sufficiently. This MAY be caused either by invoking them both in the same payload, or following one after another by CID reference. Variables relative to the result of some other action MAY be used. In this case, the attested (signed) receipt of the previous action MUST be included in the following form: Refeernced by invocation CID -### 4.1 IPLD Schema +## 5.1 IPLD Schema + + ``` ipldsch type RelativeOutput struct { @@ -222,7 +255,7 @@ type RelativeOutput struct { type Path String ``` -### 4.2 JSON Example +## 5.2 JSON Example Some alternates: @@ -256,11 +289,15 @@ Inside a next UCAN, substitution of a previous unresolved step MUST be represent NOTE security, becase the aud controls the receipt of the first part of the pipeline, they control anything under the example.com namespace -# 5 Appendix +# 6 Appendix -## 5.1 Support Types +## 6.1 Support Types ``` ipldsch +type CID = String +type URI = String +type Ability = String + type SemVer struct { num NumVer tag nullable optional String @@ -277,3 +314,5 @@ type NumVer struct { } ``` + + From 096339a052a27a625becf22a91222f5f35fb1061 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 13:18:54 -0800 Subject: [PATCH 013/154] Major blocks in place! --- README.md | 158 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 114 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 51f90c31..0034bce9 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,9 @@ type Scope enum { } ``` -## 3.3 JSON Exmaple +## 3.3 JSON Examples + +### 3.3.1 Simple ``` json { @@ -152,6 +154,88 @@ type Scope enum { } ``` +### 3.3.2 Internally Pipelined + + ``` json +{ + "ucan/invoke": "bafyUCAN", + "v": "0.1.0", + "nnc": "abcdef", + "ext": null, + "run": { + "dns://example.com?TYPE=TXT": { + "crud/update": [ + { + "value": "hello world", + "retries": 5 + } + ] + }, + "http://example.com/report": { + "http/post": [ + {"updateTo": {"ucan/promise": ["/", "dns://exmaple.com?TYPE=TXT", "crud/update", "http/statusCode"]} + ] + }, + "mailto://alice@example.com": { + "msg/send": [ + { + "to": "bob@example.com", + "subject": "DNSLink for example.com", + "body": {"ucan/promise": ["/", "dns://exmaple.com?TYPE=TXT", "crud/update", "http/body"]} + } + ] + } + }, + "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT+SRK6v/SX8bjt+VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" +} +``` + +### 3.3.3 Externally Pipelined + + ``` json +{ + "ucan/invoke": "bafyUCAN", + "v": "0.1.0", + "nnc": "abcdef", + "ext": null, + "run": { + "dns://example.com?TYPE=TXT": { + "crud/update": [ + { + "value": "hello world", + "retries": 5 + } + ] + } + }, + "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT+SRK6v/SX8bjt+VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" +} + +{ + "ucan/invoke": "bafyUCAN", + "v": "0.1.0", + "nnc": "abcdef", + "ext": null, + "run": { + "http://example.com/report": { + "http/post": [ + {"updateTo": {"ucan/promise": ["QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", "dns://exmaple.com?TYPE=TXT", "crud/update", "http/statusCode"]} + ] + }, + "mailto://alice@example.com": { + "msg/send": [ + { + "to": "bob@example.com", + "subject": "DNSLink for example.com", + "body": {"ucan/promise": ["QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", "dns://exmaple.com?TYPE=TXT", "crud/update", "http/body"]} + } + ] + } + }, + "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT+SRK6v/SX8bjt+VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" +} +``` + # 4 Receipt An invocation receipt is an attested result about the output of an invocation. A receipt MUST be attested via signature of the principal (the `aud` of the associated UCAN). @@ -226,14 +310,23 @@ At UCAN creation time, the UCAN MAY not yet have all of the information required The authority to execute later actions often cannot be fully attenuated in advance, since the executor controls the reported output of the prior step in a pipeline. When chosing to use pipelining, the invoker MUST delegate capabilities for any of the possible outputs. If tight control is required to limit authority, pipelining SHOULD NOT be used. +A promise MUST contain the CID of the target invocation, and the path of the +becaus eth eresource may have a path in it, the resource needs to be broken out! +Inverts the version from the outer invocation +## 5.1 Fields - +| Field | Type | Description | Required | +|-----------------|-----------|---------------------------------|----------| +| `ucan/promised` | `CID` | The invocation being referenced | Yes | +| `using` | `URI` | The resource called | Yes | +| `called` | `Ability` | The ability used | Yes | +| `path` | `Path` | Path to the specific output | Yes | +``` json +``` the know the concrete value required to scope the resource down sufficiently. This MAY be caused either by invoking them both in the same payload, or following one after another by CID reference. @@ -242,53 +335,32 @@ Variables relative to the result of some other action MAY be used. In this case, Refeernced by invocation CID -## 5.1 IPLD Schema - - +## 5.2 IPLD Schema ``` ipldsch -type RelativeOutput struct { - i &Invocation - o Path -- output path -} - -type Path String +type Promise struct { + pse &Invocation + usg URI + cll Ability -- output path + pth Path +} representation tuple ``` -## 5.2 JSON Example - -Some alternates: +## 5.3 JSON Example ``` json -{ - "ucan/invoked": "bafy12345", - "output": "example/a/b" -} - -"ucan:out:bafy12345/example/a/b" - +// IPLD +["bafyInvocation", "exmaple.com/foo/bar", "http/put", "http/statusCode"] -``` - -Inside a next UCAN, substitution of a previous unresolved step MUST be represented as: - -``` js -{ - // ..., - "att": { - "example.com/$PATH": { - "$PATH": { - "ucan/invoked": "bafy12345", - "output": "example.com/a/b" - } - } - } +// Full struct representation +{ + "ucan/promise": "bafyInvocation", + "using": "example.com/foo/bar", + "called": "http/put", + "path": "http/status" } ``` -NOTE security, becase the aud controls the receipt of the first part of the pipeline, they control anything under the example.com namespace - - # 6 Appendix ## 6.1 Support Types @@ -297,6 +369,7 @@ NOTE security, becase the aud controls the receipt of the first part of the pipe type CID = String type URI = String type Ability = String +type Path = String type SemVer struct { num NumVer @@ -313,6 +386,3 @@ type NumVer struct { join "." } ``` - - - From ad1ec204953b56c3adcd2ba1d6b649d159630834 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 13:28:33 -0800 Subject: [PATCH 014/154] Tighten up examples --- README.md | 59 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 0034bce9..75f9786e 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ type Scope enum { "v": "0.1.0", "nnc": "abcdef", "ext": null, - "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT+SRK6v/SX8bjt+VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" + "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" } ``` @@ -186,7 +186,7 @@ type Scope enum { ] } }, - "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT+SRK6v/SX8bjt+VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" + "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" } ``` @@ -208,7 +208,7 @@ type Scope enum { ] } }, - "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT+SRK6v/SX8bjt+VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" + "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" } { @@ -244,19 +244,21 @@ Note that this does not guarantee correctness of the result! The statement's ver ## 4.1 Fields -| Field | Type | Value | Descrption | Required | Default | -|----------------|---------------------------|-----------|--------------------------------------------------|----------|---------| -| `ucan/receipt` | `{CID => {"URI" => Any}}` | | The CID of the UCAN to invoke | Yes | | -| `v` | `SemVer` | `"0.1.0"` | SemVer of the UCAN invocation object schema | Yes | | -| `nnc` | `String` | | A unique nonce, to distinguish each receipt | Yes | | -| `ext` | `Any` | | Nonnormative extended fields | No | `null` | -| `sig` | `Bytes` | | Signature of the rest of the field canonicalized | Yes | | +| Field | Type | Descrption | Required | Default | +|----------------|--------------------------|---------------------------------------------------|----------|---------| +| `ucan/receipt` | `CID` | CID of the Invocation that generated this respose | Yes | | +| `rlt` | `{URI : {Ability: Any}}` | The results of each call | Yes | | +| `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | +| `nnc` | `String` | A unique nonce, to distinguish each receipt | Yes | | +| `ext` | `Any` | Non-normative extended fields | No | `null` | +| `sig` | `Bytes` | Signature of the rest of the field canonicalized | Yes | | ## 4.1 IPLD ``` ipldsch type Receipt struct { - rec {CID : {URI : {Ability : {String : Any}}} + rec &Invocation + rlt {URI : {Ability : Any}} v SemVer nnc String ext optional Any @@ -268,35 +270,32 @@ type Receipt struct { ``` json { - "ucan/receipt": { - "bafyLeft": { - "example.com": { - "msg/read": [ - "from": "alice@example.com", - "text": "hello world" - ] - } + "ucan/receipt": "QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", + "v": "0.1.0", + "rlt": { + "example.com": { + "msg/read": [ + "from": "alice@example.com", + "text": "hello world" + ] }, - "bafyRight": { - "sub.example.com?TYPE=TXT": { - "crud/update": { - "12345": { - "http": { - "status": 200 - }, - "value": "lorem ipsum" - } + "sub.example.com?TYPE=TXT": { + "crud/update": { + "12345": { + "http": { + "status": 200 + }, + "value": "lorem ipsum" } } } }, - "v": "0.1.0", "nnc": "xyz", "ext": { "notes": "very receipt. such wow.", "tags": ["dag-house", "fission"] }, - "sig": 0xB00 + "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt_VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" } ``` From 38f7d2f56c76f9ed85d389fe5c47a9f786f1e0f9 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 13:33:54 -0800 Subject: [PATCH 015/154] JSON typos --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 75f9786e..20e86075 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ type Scope enum { }, "http://example.com/report": { "http/post": [ - {"updateTo": {"ucan/promise": ["/", "dns://exmaple.com?TYPE=TXT", "crud/update", "http/statusCode"]} + {"updateTo": {"ucan/promise": ["/", "dns://exmaple.com?TYPE=TXT", "crud/update", "http/statusCode"]}} ] }, "mailto://alice@example.com": { @@ -219,7 +219,7 @@ type Scope enum { "run": { "http://example.com/report": { "http/post": [ - {"updateTo": {"ucan/promise": ["QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", "dns://exmaple.com?TYPE=TXT", "crud/update", "http/statusCode"]} + {"updateTo": {"ucan/promise": ["QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", "dns://exmaple.com?TYPE=TXT", "crud/update", "http/statusCode"]}} ] }, "mailto://alice@example.com": { @@ -274,10 +274,13 @@ type Receipt struct { "v": "0.1.0", "rlt": { "example.com": { - "msg/read": [ + "msg/read": [{ "from": "alice@example.com", - "text": "hello world" - ] + "text": "Hello world!" + }, { + "from": "bob@example.com", + "text": "What's up?" + }] }, "sub.example.com?TYPE=TXT": { "crud/update": { From f6f265844ee409c646592a15223a91f9c716a6e3 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 13:36:40 -0800 Subject: [PATCH 016/154] SO MANY TYPOS --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 20e86075..00a4559e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ## Depends On -* [UCAN](https://github.com/ucan-wg/spec/) >=v0.10 +* [UCAN](https://github.com/ucan-wg/spec/) * [`ucan-ipld`](https://github.com/ucan-wg/ucan-ipld/) # 0 Abstract @@ -173,7 +173,7 @@ type Scope enum { }, "http://example.com/report": { "http/post": [ - {"updateTo": {"ucan/promise": ["/", "dns://exmaple.com?TYPE=TXT", "crud/update", "http/statusCode"]}} + {"updateTo": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/statusCode"]}} ] }, "mailto://alice@example.com": { @@ -181,7 +181,7 @@ type Scope enum { { "to": "bob@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "dns://exmaple.com?TYPE=TXT", "crud/update", "http/body"]} + "body": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} } ] } @@ -219,7 +219,7 @@ type Scope enum { "run": { "http://example.com/report": { "http/post": [ - {"updateTo": {"ucan/promise": ["QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", "dns://exmaple.com?TYPE=TXT", "crud/update", "http/statusCode"]}} + {"updateTo": {"ucan/promise": ["QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", "dns://example.com?TYPE=TXT", "crud/update", "http/0/statusCode"]}} ] }, "mailto://alice@example.com": { @@ -227,7 +227,7 @@ type Scope enum { { "to": "bob@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", "dns://exmaple.com?TYPE=TXT", "crud/update", "http/body"]} + "body": {"ucan/promise": ["QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", "dns://example.com?TYPE=TXT", "crud/update", "http/0/body"]} } ] } @@ -352,7 +352,7 @@ type Promise struct { ``` json // IPLD -["bafyInvocation", "exmaple.com/foo/bar", "http/put", "http/statusCode"] +["bafyInvocation", "example.com/foo/bar", "http/put", "http/statusCode"] // Full struct representation { From 8b3735afccdc9a320e7de0538e19b90c820b5fdc Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 13:54:03 -0800 Subject: [PATCH 017/154] Diamond pipeline example --- README.md | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 00a4559e..354e7069 100644 --- a/README.md +++ b/README.md @@ -171,9 +171,14 @@ type Scope enum { } ] }, - "http://example.com/report": { - "http/post": [ - {"updateTo": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/statusCode"]}} + "https://example.com/report": { + "crud/update": [ + { + "performedBy": "did:key:zAlice", + "tags": ["hello", "world"], + "status": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/statusCode"]}}, + "payload": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} + } ] }, "mailto://alice@example.com": { @@ -184,6 +189,20 @@ type Scope enum { "body": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} } ] + }, + "https://example.com/events": { + "crud/create": [ + { + "event": "update-dns", + "status": "done" + }, + { + "_": [ + {"ucan/promise": ["/", "mailto://alice@example.com", "msg/send", null]} + {"ucan/promise": ["/", "https://example.com", "crud/update", null]} + ] + } + ] } }, "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" @@ -273,7 +292,7 @@ type Receipt struct { "ucan/receipt": "QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", "v": "0.1.0", "rlt": { - "example.com": { + "https://example.com": { "msg/read": [{ "from": "alice@example.com", "text": "Hello world!" @@ -282,7 +301,7 @@ type Receipt struct { "text": "What's up?" }] }, - "sub.example.com?TYPE=TXT": { + "dns://sub.example.com?TYPE=TXT": { "crud/update": { "12345": { "http": { From e6f0c8ca7cb72ae3a96d21567cefa76cf65d3b37 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 18:09:29 -0800 Subject: [PATCH 018/154] Might need to simplify that explination, but it gets the job done for now! --- README.md | 121 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 354e7069..8e494eb0 100644 --- a/README.md +++ b/README.md @@ -22,28 +22,44 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S # 1 Introduction -## 1.1 Motivation +UCAN is a chained-capability certificate. It contains all of the information that one would need to perform some action, and the provable authority to do so. This begs the question: can UCAN be used directly as an RPC language? -Why separate the UCAN from the invocation format? Surely the UCAN itself already contains all the infomation required. +This is possible to use a UCAN directly as RPC when the intention is clear from context. This generally requires by putting more information on the channel than the UCAN itself (such as an HTTP path that a UCAN is sent to). The kinds of contextual information and features that this enabled are explored below. + +## 1.1 Intuition -A UCAN contains everythiung needed to execute a request +In a referentially transparent setting, the authority to perform some action is equalivalent to having done so: a function and its results are interchangable. [Programming languages with call-by-need semantics](https://en.wikipedia.org/wiki/Haskell) have shown that this can be a very straightforward programming model for pure computation. Effects under this model requires special handling, and whwn something will run can sometimes be unclear. -"intention" +Most languages use eager evaluation, and thus must contend directly with the distinction between a referance to a function and a command to run it. For instance, in JavaScript, adding parantheses to a function will run it. Omitting them lets the program pass around a reference to the function without immedietly invoking it. -Dleegation and execution are opposite pointing arrows +``` js +const message = () => alert("hello world") +message // Nothing happens +message() // Message interups user +``` -## 1.2 Separation +Delegating a capability is like the statement `message`. Invocation is like `message()`. It's true that sometimes we know to run things from their surrounding context without the parentheses: -I would argue that this is not the case for a few reasons. +```js +[1,2,3].map(message) // Message runs 3 times +``` -1. Authority is not intent to execute. Authority transfer is first-class in UCAN, other actions are not -2. Mixing the two levels means that invocation info will live with the token (as a proof) on subdelegations -3. Other detail, such as scheduling +However, there is clearly a distinction between passing a function and invoking it. -## 1.3 Authority Is Not Intention +The same is true for capabilties: delegating the authority to do something is not the same as asking for it to be done immeditely, even if sometimes it's clear from context. + +## 1.2 Intent + +> Just because you can doesn't mean that you should +> +> — Anon "Just because you can, doens't mean you should". Granting a UCAN to a peer means that they are allowed to perform some actions for a period of time. This is a feature not a bug, but also says nothing about the intention of when it should be run. I may grant a collaborative process the ability to perform actions on an ongoing basis (hmm, but vioating POLA) +1. Authority is not intent to execute. Authority transfer is first-class in UCAN, other actions are not +2. Mixing the two levels means that invocation info will live with the token (as a proof) on subdelegations +3. Pipelining + I could preload the service with `n` UCANs with narrow time windows and `nbf`s in the future. That would certainly be very secure, but it would be less convenient since I need to come online to issue more or to # 2 Roles @@ -69,16 +85,6 @@ The executor MUST be the UCAN delegate. Their DID MUST be set the in `aud` field # 3 Envelope - - The invocation envelope is a thin wrapper around a UCAN that conveys that all of the contained UCAN actions SHOULD be performed. All of the roles from the referenced UCANs MUST persist to the invocation per the [above table](FIXME). @@ -255,6 +261,32 @@ type Scope enum { } ``` +# 4 Isolated Capabilities + +It is often important to be able to reference a specific capability in isolation, disentangling it from the its sibling capabilities and other configuration. This is important for being able to reference a specific Capabilty in Results, promise pipelining, logging, and for building external caches. + +| Field | Type | Description | Required | +|-------|----------|--------------------------------------------------------|----------| +| `res` | `URI` | The (canonicalized) URI of the resource being accessed | Yes | +| `aby` | `String` | The lowercase Ability called on the resource | Yes | +| `ins` | `[Any]` | Any other inputs required for the call | Yes | + +``` ipldsch +type Capability struct { + rsc URI -- Resource + aby Ability -- Ability + ins [Any] -- Inputs +} +``` + +``` json +{ + "rsc": "dns://_dnslink.example.com?TYPE=TXT", + "aby": "crud/update", + "ins": [{"value": "QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f"}] +} +``` + # 4 Receipt An invocation receipt is an attested result about the output of an invocation. A receipt MUST be attested via signature of the principal (the `aud` of the associated UCAN). @@ -263,14 +295,23 @@ Note that this does not guarantee correctness of the result! The statement's ver ## 4.1 Fields -| Field | Type | Descrption | Required | Default | -|----------------|--------------------------|---------------------------------------------------|----------|---------| -| `ucan/receipt` | `CID` | CID of the Invocation that generated this respose | Yes | | -| `rlt` | `{URI : {Ability: Any}}` | The results of each call | Yes | | -| `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | -| `nnc` | `String` | A unique nonce, to distinguish each receipt | Yes | | -| `ext` | `Any` | Non-normative extended fields | No | `null` | -| `sig` | `Bytes` | Signature of the rest of the field canonicalized | Yes | | +| Field | Type | Description | Required | Default | +|----------------|--------------|----------------------------------------------------------------------------------|----------|---------| +| `ucan/receipt` | `CID` | CID of the Invocation that generated this respose | Yes | | +| `rlt` | `{CID: Any}` | The results of each call, indexed by the CID of the [isolated capability](FIXME) | Yes | | +| `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | +| `nnc` | `String` | A unique nonce, to distinguish each receipt | Yes | | +| `ext` | `Any` | Non-normative extended fields | No | `null` | +| `sig` | `Bytes` | Signature of the rest of the field canonicalized | Yes | | + +``` json +{ + "QmCapability": { + "http/status": 200, + "http/payload": "hello world" + } +} +``` ## 4.1 IPLD @@ -292,8 +333,7 @@ type Receipt struct { "ucan/receipt": "QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", "v": "0.1.0", "rlt": { - "https://example.com": { - "msg/read": [{ + "QmYqbJBxCqqzDouPMbcrbNuB3WHWajSyq4RWin7ufs2ajf": [{ "from": "alice@example.com", "text": "Hello world!" }, { @@ -301,15 +341,12 @@ type Receipt struct { "text": "What's up?" }] }, - "dns://sub.example.com?TYPE=TXT": { - "crud/update": { - "12345": { - "http": { - "status": 200 - }, - "value": "lorem ipsum" - } - } + "QmXrfqKNUpRiyyi8r3hpSZyZuG3S2MY9rTw1p8iEC9FNh5": { + "http": { + "status": 200, + "body": "hello world" + }, + "ms": 476 } }, "nnc": "xyz", @@ -321,6 +358,7 @@ type Receipt struct { } ``` + # 5 Promise Pipelining > 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 @@ -346,9 +384,6 @@ Inverts the version from the outer invocation | `called` | `Ability` | The ability used | Yes | | `path` | `Path` | Path to the specific output | Yes | -``` json -``` - the know the concrete value required to scope the resource down sufficiently. This MAY be caused either by invoking them both in the same payload, or following one after another by CID reference. Variables relative to the result of some other action MAY be used. In this case, the attested (signed) receipt of the previous action MUST be included in the following form: From d70988ae5b9c703a4dbe2b48fb584fa18acfd341 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 18:39:32 -0800 Subject: [PATCH 019/154] Intro done minus links --- README.md | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 8e494eb0..c0236bbe 100644 --- a/README.md +++ b/README.md @@ -22,20 +22,32 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S # 1 Introduction +> Just because you can doesn't mean that you should +> +> — Anon + UCAN is a chained-capability certificate. It contains all of the information that one would need to perform some action, and the provable authority to do so. This begs the question: can UCAN be used directly as an RPC language? -This is possible to use a UCAN directly as RPC when the intention is clear from context. This generally requires by putting more information on the channel than the UCAN itself (such as an HTTP path that a UCAN is sent to). The kinds of contextual information and features that this enabled are explored below. - +It is possible to use a UCAN directly for RPC when the intention is clear from context. This generally requires by putting more information on the channel than the UCAN itself (such as an HTTP path that a UCAN is sent to). Invocation includes strictly more information than delegation: all of the authorty, plus the command to perform the action. + ## 1.1 Intuition +## 1.1.1 Car Keys + +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 Alice's car whenever he wants to. Depending on their plans for the rest of the day, Akiko may find Boris quite rude if he immedietly leaves the cafe to drive her car. 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 immedietly drive off. + +## 1.1.2 Lazy vs Eager Evaluation + In a referentially transparent setting, the authority to perform some action is equalivalent to having done so: a function and its results are interchangable. [Programming languages with call-by-need semantics](https://en.wikipedia.org/wiki/Haskell) have shown that this can be a very straightforward programming model for pure computation. Effects under this model requires special handling, and whwn something will run can sometimes be unclear. -Most languages use eager evaluation, and thus must contend directly with the distinction between a referance to a function and a command to run it. For instance, in JavaScript, adding parantheses to a function will run it. Omitting them lets the program pass around a reference to the function without immedietly invoking it. +Most languages use eager evaluation. Eager languages must contend directly with the distinction between a referance to a function and a command to run it. For instance, in JavaScript, adding parantheses to a function will run it. Omitting them lets the program pass around a reference to the function without immedietly invoking it. ``` js const message = () => alert("hello world") -message // Nothing happens -message() // Message interups user +message // Nothing happens +message() // A message interups the user ``` Delegating a capability is like the statement `message`. Invocation is like `message()`. It's true that sometimes we know to run things from their surrounding context without the parentheses: @@ -48,19 +60,11 @@ However, there is clearly a distinction between passing a function and invoking The same is true for capabilties: delegating the authority to do something is not the same as asking for it to be done immeditely, even if sometimes it's clear from context. -## 1.2 Intent - -> Just because you can doesn't mean that you should -> -> — Anon - -"Just because you can, doens't mean you should". Granting a UCAN to a peer means that they are allowed to perform some actions for a period of time. This is a feature not a bug, but also says nothing about the intention of when it should be run. I may grant a collaborative process the ability to perform actions on an ongoing basis (hmm, but vioating POLA) +## 1.3 Order of Evaluation -1. Authority is not intent to execute. Authority transfer is first-class in UCAN, other actions are not -2. Mixing the two levels means that invocation info will live with the token (as a proof) on subdelegations -3. Pipelining +Information about the scheduling, order, and [pipelining](FIXME) of actions is orthogonal to the flow of authority. -I could preload the service with `n` UCANs with narrow time windows and `nbf`s in the future. That would certainly be very secure, but it would be less convenient since I need to come online to issue more or to +As we shall see in the [promise pipelining section](FIXME), asking an agent to perform a sequence of actions before you know the exact parameters requires delegating capabilties 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 authorty that the executor has (since they can claim any value as having returned for any step). # 2 Roles From 64cd3f825edcc80be3a2bd6c592539a21f6f30e9 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 18:46:16 -0800 Subject: [PATCH 020/154] Cleaning up --- README.md | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index c0236bbe..3199865e 100644 --- a/README.md +++ b/README.md @@ -114,9 +114,11 @@ The `ucan/invoke` field MUST contain CIDs pointing to the UCANs to invoke. The o The `v` field MUST contain the version of the invocation object schema. -### 3.1.3 Capabilities +### 3.1.3 Run Capabilities -The OPTIONAL `run` field specifies which actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](FIXME)) are intended to be run, then they MUST be treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or lesser scope. +The OPTIONAL `run` field MUST reference the actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](FIXME)) are intended to be run, then they MUST be treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or lesser scope. + +The only difference for the attenuated case is that [promises](FIXME) MAY also be used as inputs to fields. ### 3.1.4 Nonce @@ -291,13 +293,13 @@ type Capability struct { } ``` -# 4 Receipt +# 5 Receipt An invocation receipt is an attested result about the output of an invocation. A receipt MUST be attested via signature of the principal (the `aud` of the associated UCAN). Note that this does not guarantee correctness of the result! The statement's veracity MUST be only understood as an attestation from the executor. -## 4.1 Fields +## 5.1 Fields | Field | Type | Description | Required | Default | |----------------|--------------|----------------------------------------------------------------------------------|----------|---------| @@ -308,16 +310,7 @@ Note that this does not guarantee correctness of the result! The statement's ver | `ext` | `Any` | Non-normative extended fields | No | `null` | | `sig` | `Bytes` | Signature of the rest of the field canonicalized | Yes | | -``` json -{ - "QmCapability": { - "http/status": 200, - "http/payload": "hello world" - } -} -``` - -## 4.1 IPLD +## 5.1 IPLD ``` ipldsch type Receipt struct { @@ -330,7 +323,7 @@ type Receipt struct { } ``` -## 4.2 JSON Example +## 5.2 JSON Example ``` json { @@ -363,7 +356,7 @@ type Receipt struct { ``` -# 5 Promise Pipelining +# 6 Promise Pipelining > 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 > @@ -379,7 +372,7 @@ becaus eth eresource may have a path in it, the resource needs to be broken out! Inverts the version from the outer invocation -## 5.1 Fields +## 6.1 Fields | Field | Type | Description | Required | |-----------------|-----------|---------------------------------|----------| @@ -395,7 +388,7 @@ Variables relative to the result of some other action MAY be used. In this case, Refeernced by invocation CID -## 5.2 IPLD Schema +## 6.2 IPLD Schema ``` ipldsch type Promise struct { @@ -406,7 +399,7 @@ type Promise struct { } representation tuple ``` -## 5.3 JSON Example +## 6.3 JSON Example ``` json // IPLD @@ -421,9 +414,9 @@ type Promise struct { } ``` -# 6 Appendix +# 7 Appendix -## 6.1 Support Types +## 7.1 Support Types ``` ipldsch type CID = String From 19d8eb0d1655e73ce5e315e04ded408b0c9d3116 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 18:50:04 -0800 Subject: [PATCH 021/154] JSON typos --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3199865e..ed9a7433 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ type Scope enum { { "performedBy": "did:key:zAlice", "tags": ["hello", "world"], - "status": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/statusCode"]}}, + "status": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/statusCode"]}, "payload": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} } ] @@ -330,14 +330,16 @@ type Receipt struct { "ucan/receipt": "QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", "v": "0.1.0", "rlt": { - "QmYqbJBxCqqzDouPMbcrbNuB3WHWajSyq4RWin7ufs2ajf": [{ + "QmYqbJBxCqqzDouPMbcrbNuB3WHWajSyq4RWin7ufs2ajf": [ + { "from": "alice@example.com", "text": "Hello world!" - }, { + }, + { "from": "bob@example.com", "text": "What's up?" - }] - }, + } + ], "QmXrfqKNUpRiyyi8r3hpSZyZuG3S2MY9rTw1p8iEC9FNh5": { "http": { "status": 200, From c29dd8a2db9a67aa0a4485c4fd3674f07c8b31b9 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 19:24:03 -0800 Subject: [PATCH 022/154] No more FIXMEs --- README.md | 70 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index ed9a7433..581013a1 100644 --- a/README.md +++ b/README.md @@ -24,19 +24,19 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S > Just because you can doesn't mean that you should > -> — Anon +> — Anonymous -UCAN is a chained-capability certificate. It contains all of the information that one would need to perform some action, and the provable authority to do so. This begs the question: can UCAN be used directly as an RPC language? +UCAN is a chained-capability format. A UCAN contains all of the information that one would need to perform some action, and the provable authority to do so. This begs the question: can UCAN be used directly as an RPC language? -It is possible to use a UCAN directly for RPC when the intention is clear from context. This generally requires by putting more information on the channel than the UCAN itself (such as an HTTP path that a UCAN is sent to). Invocation includes strictly more information than delegation: all of the authorty, plus the command to perform the action. +Some teams have had success using UCAN directly for RPC when the intention is clear from context. This occurs when there is more information on the channel than the UCAN itself (such as an HTTP path that a UCAN is sent to). Capability invocation contains strictly more information than delegation: all of the authorty of UCAN, plus the command to perform the action. ## 1.1 Intuition ## 1.1.1 Car Keys -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. +Consider the following fictitious scenario: -Boris now has the capability to drive Alice's car whenever he wants to. Depending on their plans for the rest of the day, Akiko may find Boris quite rude if he immedietly leaves the cafe to drive her car. 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 immedietly drive off. +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 Alice's car whenever he wants to. Depending on their plans for the rest of the day, Akiko may find Boris quite rude if he immedietly leaves the cafe to drive her car. 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 immedietly drive off. ## 1.1.2 Lazy vs Eager Evaluation @@ -50,21 +50,19 @@ message // Nothing happens message() // A message interups the user ``` -Delegating a capability is like the statement `message`. Invocation is like `message()`. It's true that sometimes we know to run things from their surrounding context without the parentheses: +Delegating a capability is like the statement `message`. Invocation is akin to `message()`. It's true that sometimes we know to run things from their surrounding context without the parentheses: ```js [1,2,3].map(message) // Message runs 3 times ``` -However, there is clearly a distinction between passing a function and invoking it. +However, there is clearly a distinction between passing a function and invoking it. The same is true for capabilties: delegating the authority to do something is not the same as asking for it to be done immeditely, even if sometimes it's clear from context. -The same is true for capabilties: delegating the authority to do something is not the same as asking for it to be done immeditely, even if sometimes it's clear from context. +## 1.3 Separation of Concerns -## 1.3 Order of Evaluation +Information about the scheduling, order, and pipelining of actions 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 action by the latest invoker. -Information about the scheduling, order, and [pipelining](FIXME) of actions is orthogonal to the flow of authority. - -As we shall see in the [promise pipelining section](FIXME), asking an agent to perform a sequence of actions before you know the exact parameters requires delegating capabilties 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 authorty that the executor has (since they can claim any value as having returned for any step). +As we shall see in the [promise pipelining section](#6-promise-pipelining), asking an agent to perform a sequence of actions before you know the exact parameters requires delegating capabilties 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 authorty that the executor has (since they can claim any value as having returned for any step). # 2 Roles @@ -91,7 +89,7 @@ The executor MUST be the UCAN delegate. Their DID MUST be set the in `aud` field The invocation envelope is a thin wrapper around a UCAN that conveys that all of the contained UCAN actions SHOULD be performed. -All of the roles from the referenced UCANs MUST persist to the invocation per the [above table](FIXME). +All of the roles from the referenced UCANs MUST persist to the invocation per the [roles table](#2-roles). The invocation wrapper MUST be signed by the same principal that issued the UCAN. @@ -116,9 +114,9 @@ The `v` field MUST contain the version of the invocation object schema. ### 3.1.3 Run Capabilities -The OPTIONAL `run` field MUST reference the actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](FIXME)) are intended to be run, then they MUST be treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or lesser scope. +The OPTIONAL `run` field MUST reference the actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](#6-promise-pipelining)) are intended to be run, then they MUST be treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or lesser scope. -The only difference for the attenuated case is that [promises](FIXME) MAY also be used as inputs to fields. +The only difference for the attenuated case is that [promises](#6-promise-pipelining) MAY also be used as inputs to fields. ### 3.1.4 Nonce @@ -158,7 +156,7 @@ type Scope enum { ``` json { - "ucan/invoke": "bafyUCAN", + "ucan/invoke": "QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "v": "0.1.0", "nnc": "abcdef", "ext": null, @@ -166,11 +164,11 @@ type Scope enum { } ``` -### 3.3.2 Internally Pipelined +### 3.3.2 Batched Pipeline ``` json { - "ucan/invoke": "bafyUCAN", + "ucan/invoke": "QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "v": "0.1.0", "nnc": "abcdef", "ext": null, @@ -221,11 +219,11 @@ type Scope enum { } ``` -### 3.3.3 Externally Pipelined +### 3.3.3 Serial Pipeline ``` json { - "ucan/invoke": "bafyUCAN", + "ucan/invoke": "Qmd4trNUhgWwsBbSsYBEWJqgiHyLrnhZ8u1DJsWsEKeuuF", "v": "0.1.0", "nnc": "abcdef", "ext": null, @@ -243,7 +241,7 @@ type Scope enum { } { - "ucan/invoke": "bafyUCAN", + "ucan/invoke": "Qmd4trNUhgWwsBbSsYBEWJqgiHyLrnhZ8u1DJsWsEKeuuF", "v": "0.1.0", "nnc": "abcdef", "ext": null, @@ -304,7 +302,7 @@ Note that this does not guarantee correctness of the result! The statement's ver | Field | Type | Description | Required | Default | |----------------|--------------|----------------------------------------------------------------------------------|----------|---------| | `ucan/receipt` | `CID` | CID of the Invocation that generated this respose | Yes | | -| `rlt` | `{CID: Any}` | The results of each call, indexed by the CID of the [isolated capability](FIXME) | Yes | | +| `rlt` | `{CID: Any}` | The results of each call, indexed by the CID of the [isolated capability](#4-isolated-capabilities) | Yes | | | `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | | `nnc` | `String` | A unique nonce, to distinguish each receipt | Yes | | | `ext` | `Any` | Non-normative extended fields | No | `null` | @@ -354,7 +352,7 @@ type Receipt struct { "tags": ["dag-house", "fission"] }, "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt_VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" -} + } ``` @@ -405,11 +403,11 @@ type Promise struct { ``` json // IPLD -["bafyInvocation", "example.com/foo/bar", "http/put", "http/statusCode"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/put", "http/statusCode"] // Full struct representation { - "ucan/promise": "bafyInvocation", + "ucan/promise": "QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "using": "example.com/foo/bar", "called": "http/put", "path": "http/status" @@ -441,3 +439,25 @@ type NumVer struct { join "." } ``` + +# 8 Prior Art + +[UCANTO](https://github.com/web3-storage/ucanto) from DAG House + +[CapTP](erights.org/elib/distrib/captp/index.html) + +[OCapN](https://github.com/ocapn/ocapn) + +[Cap'N Proto](https://capnproto.org/) + +[Spritely Goblins](https://spritely.institute/static/papers/spritely-core.html) + +# 9 Acknowledgements + +Thanks to the [DAG House](https://dag.house) team for initially suggesting UCAN as a generalized RPC framework. + +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/). + +Thanks to [Christine Lemmer-Webber](https://github.com/cwebber) for the many conversations about capabaility systems and the programming models that they enable. + +Thanks to [Quinn Wilton](https://github.com/QuinnWilton) and [Marc-Antoine Parent](https://github.com/maparent) for their discussion of the distinction between declarations and directives. From 1ac3b5a7e96606508012cf16ddbbbad1888d94e4 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 19:28:28 -0800 Subject: [PATCH 023/154] Typos --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 581013a1..76f4ab48 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S UCAN is a chained-capability format. A UCAN contains all of the information that one would need to perform some action, 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 using UCAN directly for RPC when the intention is clear from context. This occurs when there is more information on the channel than the UCAN itself (such as an HTTP path that a UCAN is sent to). Capability invocation contains strictly more information than delegation: all of the authorty of UCAN, plus the command to perform the action. +Some teams have had success using 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 authorty of UCAN, plus the command to perform the action. ## 1.1 Intuition @@ -36,11 +36,11 @@ Some teams have had success using UCAN directly for RPC when the intention is cl 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 Alice's car whenever he wants to. Depending on their plans for the rest of the day, Akiko may find Boris quite rude if he immedietly leaves the cafe to drive her car. 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 immedietly drive off. +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 Alice's car whenever he wants to. Depending on their plans for the rest of the day, Akiko may find Boris quite rude if he immedietly 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 immedietly drive off. ## 1.1.2 Lazy vs Eager Evaluation -In a referentially transparent setting, the authority to perform some action is equalivalent to having done so: a function and its results are interchangable. [Programming languages with call-by-need semantics](https://en.wikipedia.org/wiki/Haskell) have shown that this can be a very straightforward programming model for pure computation. Effects under this model requires special handling, and whwn something will run can sometimes be unclear. +In a referentially transparent setting, the description of an action is equalivalent to having done so: a function and its results are interchangable. [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. Effects under this model requires special handling, and when something will run can sometimes be unclear. Most languages use eager evaluation. Eager languages must contend directly with the distinction between a referance to a function and a command to run it. For instance, in JavaScript, adding parantheses to a function will run it. Omitting them lets the program pass around a reference to the function without immedietly invoking it. From 892516d6e04173ebba0751dee6ebdfb0716d9002 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 19:31:15 -0800 Subject: [PATCH 024/154] Change spellchecker --- .github/workflows/spellcheck.yml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index 06710ca3..efc1065a 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -1,12 +1,15 @@ -name: Spellcheck Action -on: push +name: 'spellcheck' +on: + pull_request: + push: jobs: - build: - name: Spellcheck + spellcheck: runs-on: ubuntu-latest steps: - # The checkout step - - uses: actions/checkout@master - - uses: rojopolis/spellcheck-github-actions@0.24.0 - name: Spellcheck + - uses: actions/checkout@v3 + - uses: matheus23/md-spellcheck-action@v4.2.2 + with: + files-to-check: "*.md" + files-to-exclude: "LICENSE.md" # optional + words-to-ignore-file: ./words-to-ignore.txt From 508d9217e263ba6b06fb0b3510a6bdf2bf824032 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 19:32:20 -0800 Subject: [PATCH 025/154] Configure spellchecker --- .github/workflows/spellcheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index efc1065a..c331f805 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -12,4 +12,4 @@ jobs: with: files-to-check: "*.md" files-to-exclude: "LICENSE.md" # optional - words-to-ignore-file: ./words-to-ignore.txt + words-to-ignore-file: ".github/workflows/words-to-ignore.txt" From 8a6945637fd518205547e9aaad441b961d56337f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 19:33:09 -0800 Subject: [PATCH 026/154] Limit to readme --- .github/workflows/spellcheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index c331f805..50a25347 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -10,6 +10,6 @@ jobs: - uses: actions/checkout@v3 - uses: matheus23/md-spellcheck-action@v4.2.2 with: - files-to-check: "*.md" + files-to-check: "README.md" files-to-exclude: "LICENSE.md" # optional words-to-ignore-file: ".github/workflows/words-to-ignore.txt" From 283583b666e400a4b334bc166f487ab000c421c3 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 19:36:28 -0800 Subject: [PATCH 027/154] Update dictionary --- .github/workflows/words-to-ignore.txt | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 71f61163..db6fd040 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -1,30 +1,45 @@ +Akiko CIDs +Cap'N +CapTP +Gozalishvili Hardt Holmgren +IPLD +Irakli JSON JWT JWT-encoded +Lemmer-Webber +OCapN Pre-Draft +Proto +RPC +SemVer +Spritely TTL UCAN UCAN's +UCANTO UCANs +URI Zelenka auth backend +de +delegator dholms expede +facto interpretable +invoker multiformat +pipelining +pre-vacation requestor's responder signalling trustless +unpadded v0 validator - -# wut -HeaderTypeDescriptionRequiredCardinalityEntrypoint -UCANNoZero -UCANYesOneMapping -HeaderTypeDescriptionRequiredCardinalityEntry From e96f18d93e1d81cc5974f83286a06ea6879e09a2 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 19:42:24 -0800 Subject: [PATCH 028/154] Typos --- .github/workflows/words-to-ignore.txt | 4 ++++ README.md | 26 +++++++++++++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index db6fd040..3a045d35 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -26,6 +26,8 @@ URI Zelenka auth backend +base64url +canonicalized de delegator dholms @@ -34,6 +36,8 @@ facto interpretable invoker multiformat +outmost +pipelined pipelining pre-vacation requestor's diff --git a/README.md b/README.md index 76f4ab48..d701c6bc 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S UCAN is a chained-capability format. A UCAN contains all of the information that one would need to perform some action, 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 using 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 authorty of UCAN, plus the command to perform the action. +Some teams have had success using 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 action. ## 1.1 Intuition @@ -36,13 +36,13 @@ Some teams have had success using UCAN directly for RPC when the intention is cl 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 Alice's car whenever he wants to. Depending on their plans for the rest of the day, Akiko may find Boris quite rude if he immedietly 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 immedietly drive off. +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 Alice'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. ## 1.1.2 Lazy vs Eager Evaluation -In a referentially transparent setting, the description of an action is equalivalent to having done so: a function and its results are interchangable. [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. Effects under this model requires special handling, and when something will run can sometimes be unclear. +In a referentially transparent setting, the description of an action is equivalent to having done so: a function and its results are interchangable. [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. Effects under this model requires special handling, and when somtheing will run can sometimes be unclear. -Most languages use eager evaluation. Eager languages must contend directly with the distinction between a referance to a function and a command to run it. For instance, in JavaScript, adding parantheses to a function will run it. Omitting them lets the program pass around a reference to the function without immedietly invoking it. +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. ``` js const message = () => alert("hello world") @@ -56,17 +56,17 @@ Delegating a capability is like the statement `message`. Invocation is akin to ` [1,2,3].map(message) // Message runs 3 times ``` -However, there is clearly a distinction between passing a function and invoking it. The same is true for capabilties: delegating the authority to do something is not the same as asking for it to be done immeditely, even if sometimes it's clear from context. +However, there is clearly a distinction between passing a function and invoking it. The same is true for capabilties: delegating the authority to do somtheing is not the same as asking for it to be done immediately, even if sometimes it's clear from context. ## 1.3 Separation of Concerns Information about the scheduling, order, and pipelining of actions 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 action by the latest invoker. -As we shall see in the [promise pipelining section](#6-promise-pipelining), asking an agent to perform a sequence of actions before you know the exact parameters requires delegating capabilties 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 authorty that the executor has (since they can claim any value as having returned for any step). +As we shall see in the [promise pipelining section](#6-promise-pipelining), asking an agent to perform a sequence of actions before you know the exact parameters requires delegating capabilties 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). # 2 Roles -Invocation adds two new roles to UCAN: invoker and executor. The existing UCAN delegator and delgate pricipals MUST persist to the invocation. +Invocation 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 | |------------|-----------------------------|-------------------------| @@ -130,7 +130,7 @@ The OPTIONAL `ext` field MAY contain arbitrary data. If not present, the `ext` f The `sig` field MUST contain the signature of the other fields. To construct the payload, the other fields MUST first be canonicalized as `dag-cbor`. -If serialzied as JSON, the `sig` field MUST be serialized as [unpadded base64url](https://datatracker.ietf.org/doc/html/rfc4648#section-5). +If serialized as JSON, the `sig` field MUST be serialized as [unpadded base64url](https://datatracker.ietf.org/doc/html/rfc4648#section-5). ## 3.2 IPLD Schema @@ -301,7 +301,7 @@ Note that this does not guarantee correctness of the result! The statement's ver | Field | Type | Description | Required | Default | |----------------|--------------|----------------------------------------------------------------------------------|----------|---------| -| `ucan/receipt` | `CID` | CID of the Invocation that generated this respose | Yes | | +| `ucan/receipt` | `CID` | CID of the Invocation that generated this response | Yes | | | `rlt` | `{CID: Any}` | The results of each call, indexed by the CID of the [isolated capability](#4-isolated-capabilities) | Yes | | | `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | | `nnc` | `String` | A unique nonce, to distinguish each receipt | Yes | | @@ -364,11 +364,11 @@ type Receipt struct { At UCAN creation time, the UCAN MAY not yet have all of the information required to construct the next request in a sequence. Waiting for each request to complete before proceeding to the next action 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 earlier invocations into the input of a later one. This liberates the invoker from waiting for each step. -The authority to execute later actions often cannot be fully attenuated in advance, since the executor controls the reported output of the prior step in a pipeline. When chosing to use pipelining, the invoker MUST delegate capabilities for any of the possible outputs. If tight control is required to limit authority, pipelining SHOULD NOT be used. +The authority to execute later actions often cannot be fully attenuated in advance, since the executor controls the reported output of the prior step in a pipeline. When choosing to use pipelining, the invoker MUST delegate capabilities for any of the possible outputs. If tight control is required to limit authority, pipelining SHOULD NOT be used. A promise MUST contain the CID of the target invocation, and the path of the -becaus eth eresource may have a path in it, the resource needs to be broken out! +because the resource may have a path in it, the resource needs to be broken out! Inverts the version from the outer invocation @@ -385,7 +385,7 @@ the know the concrete value required to scope the resource down sufficiently. T Variables relative to the result of some other action MAY be used. In this case, the attested (signed) receipt of the previous action MUST be included in the following form: -Refeernced by invocation CID +Referenced by invocation CID ## 6.2 IPLD Schema @@ -458,6 +458,6 @@ Thanks to the [DAG House](https://dag.house) team for initially suggesting UCAN 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/). -Thanks to [Christine Lemmer-Webber](https://github.com/cwebber) for the many conversations about capabaility 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 [Quinn Wilton](https://github.com/QuinnWilton) and [Marc-Antoine Parent](https://github.com/maparent) for their discussion of the distinction between declarations and directives. From 25569294f6eda7bf418c25f36fd8a3c6558d2073 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 19:46:55 -0800 Subject: [PATCH 029/154] typos typos typos --- .github/workflows/words-to-ignore.txt | 3 +++ README.md | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 3a045d35..29074a18 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -1,3 +1,4 @@ +Acknowledgements Akiko CIDs Cap'N @@ -40,10 +41,12 @@ outmost pipelined pipelining pre-vacation +referentially requestor's responder signalling trustless unpadded +url v0 validator diff --git a/README.md b/README.md index d701c6bc..d9ad42c7 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,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 an action is equivalent to having done so: a function and its results are interchangable. [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. Effects under this model requires special handling, and when somtheing will run can sometimes be unclear. +In a referentially transparent setting, the description of an action is equivalent to having done so: a function and its results are interchangable. [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. Effects under this model requires special handling, and 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. @@ -56,13 +56,13 @@ Delegating a capability is like the statement `message`. Invocation is akin to ` [1,2,3].map(message) // Message runs 3 times ``` -However, there is clearly a distinction between passing a function and invoking it. The same is true for capabilties: delegating the authority to do somtheing is not the same as asking for it to be done immediately, even if sometimes it's clear from context. +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.3 Separation of Concerns Information about the scheduling, order, and pipelining of actions 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 action by the latest invoker. -As we shall see in the [promise pipelining section](#6-promise-pipelining), asking an agent to perform a sequence of actions before you know the exact parameters requires delegating capabilties 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 [promise pipelining section](#6-promise-pipelining), asking an agent to perform a sequence of actions 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). # 2 Roles @@ -95,13 +95,13 @@ The invocation wrapper MUST be signed by the same principal that issued the UCAN ## 3.1 Fields -| Field | Type | Value | Descrption | Required | Default | +| Field | Type | Value | Description | Required | Default | |---------------|------------------------------------|-----------|--------------------------------------------------|----------|---------| | `ucan/invoke` | `CID` | | The CID of the UCAN to invoke | Yes | | | `v` | `SemVer` | `"0.1.0"` | SemVer of the UCAN invocation object schema | Yes | | | `run` | `"*" or {URI : {Ability : [Any]}}` | | Which UCAN capabilities to run | No | `"*"` | | `nnc` | `String` | | A unique nonce, to distinguish each invocation | Yes | | -| `ext` | `Any` | | Nonnormative extended fields | No | `null` | +| `ext` | `Any` | | Non-normative extended fields | No | `null` | | `sig` | `Bytes` | | Signature of the rest of the field canonicalized | Yes | | ### 3.1.1 Invocation @@ -267,7 +267,7 @@ type Scope enum { # 4 Isolated Capabilities -It is often important to be able to reference a specific capability in isolation, disentangling it from the its sibling capabilities and other configuration. This is important for being able to reference a specific Capabilty in Results, promise pipelining, logging, and for building external caches. +It is often important to be able to reference a specific capability in isolation, disentangling it from the its sibling capabilities and other configuration. This is important for being able to reference a specific Capability in Results, promise pipelining, logging, and for building external caches. | Field | Type | Description | Required | |-------|----------|--------------------------------------------------------|----------| From 7e6f2d6d1814b5537d525cc7a637a3af3f825b96 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 19:48:03 -0800 Subject: [PATCH 030/154] Last one? --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d9ad42c7..ff001572 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,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 an action is equivalent to having done so: a function and its results are interchangable. [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. Effects under this model requires special handling, and when something will run can sometimes be unclear. +In a referentially transparent setting, the description of an action 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. Effects under this model requires special handling, and 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. From e8ae3c9ed6183dfc34e622751337c65da8963ef4 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 19:50:04 -0800 Subject: [PATCH 031/154] Configure link checker --- .github/workflows/linkcheck.cfg.json | 10 ++++++++++ .github/workflows/linkcheck.yml | 1 + 2 files changed, 11 insertions(+) create mode 100644 .github/workflows/linkcheck.cfg.json diff --git a/.github/workflows/linkcheck.cfg.json b/.github/workflows/linkcheck.cfg.json new file mode 100644 index 00000000..1d3eb1eb --- /dev/null +++ b/.github/workflows/linkcheck.cfg.json @@ -0,0 +1,10 @@ +{ + "ignorePatterns": [ + { + "pattern": "https://share.ansi.org/Shared%20Documents/Standards%20Activities/American%20National%20Standards/Procedures,%20Guides,%20and%20Forms/2020_ANSI_Essential_Requirements.pdf" + }, + { + "pattern": "https://share.ansi.org/Shared%20Documents/Standards%20Activities/American%20National%20Standards/Procedures,%20Guides,%20and%20Forms/2020_ANSI_Essential_Requirements.pdf" + } + ] +} diff --git a/.github/workflows/linkcheck.yml b/.github/workflows/linkcheck.yml index b25de1f7..998d61a3 100644 --- a/.github/workflows/linkcheck.yml +++ b/.github/workflows/linkcheck.yml @@ -12,3 +12,4 @@ jobs: use-quiet-mode: 'yes' check-modified-files-only: 'yes' base-branch: 'main' + config-file: './.github/workflows/linkcheck.cfg.json' From b4659f8101dd69ef1ce57ce36dbb2459af1524b4 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 19:51:32 -0800 Subject: [PATCH 032/154] =?UTF-8?q?The=20eRights=20site=20doesn't=20have?= =?UTF-8?q?=20TLS=20=F0=9F=A4=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ff001572..ff93986d 100644 --- a/README.md +++ b/README.md @@ -444,7 +444,7 @@ type NumVer struct { [UCANTO](https://github.com/web3-storage/ucanto) from DAG House -[CapTP](erights.org/elib/distrib/captp/index.html) +[CapTP](http://erights.org/elib/distrib/captp/index.html) [OCapN](https://github.com/ocapn/ocapn) From f48bfc7b9327494d89c392e7d4a898366dca674b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 19:58:29 -0800 Subject: [PATCH 033/154] More text in roels table --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ff93986d..59a61df9 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,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 an action 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. Effects under this model requires special handling, and when something will run can sometimes be unclear. +In a referentially transparent setting, the description of an action 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. 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. @@ -62,18 +62,16 @@ However, there is clearly a distinction between passing a function and invoking Information about the scheduling, order, and pipelining of actions 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 action by the latest invoker. -As we shall see in the [promise pipelining section](#6-promise-pipelining), asking an agent to perform a sequence of actions 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](#6-promise-pipelining), asking an agent to perform a sequence of actions 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). # 2 Roles Invocation 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` | Transfer authority (active) | Request action (active) | -| `aud` | Receive authority (passive) | Perform action (active) | - -## 2.1 Invoker +| UCAN Field | Delegation | Invocation | +|------------|----------------------------------------|-----------------------------------| +| `iss` | Delegator: transfer authority (active) | Invoker: request action (active) | +| `aud` | Delegate: gain authority (passive) | Executor: perform action (active) | The invoker signals to the executor that a task associated with a UCAN SHOULD be performed. From e88800deab4b1e3323065ab92917b3f7cdf186d8 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 20:05:01 -0800 Subject: [PATCH 034/154] Syntax highlight IPLD Schemas --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 59a61df9..532c39e2 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ If serialized as JSON, the `sig` field MUST be serialized as [unpadded base64url ## 3.2 IPLD Schema -``` ipldsch +``` haskell type Invocation struct { inv &UCAN -- The UCAN providing authority v SemVer -- Version @@ -273,7 +273,7 @@ It is often important to be able to reference a specific capability in isolation | `aby` | `String` | The lowercase Ability called on the resource | Yes | | `ins` | `[Any]` | Any other inputs required for the call | Yes | -``` ipldsch +``` haskell type Capability struct { rsc URI -- Resource aby Ability -- Ability @@ -308,7 +308,7 @@ Note that this does not guarantee correctness of the result! The statement's ver ## 5.1 IPLD -``` ipldsch +``` haskell type Receipt struct { rec &Invocation rlt {URI : {Ability : Any}} @@ -388,7 +388,7 @@ Referenced by invocation CID ## 6.2 IPLD Schema -``` ipldsch +``` haskell type Promise struct { pse &Invocation usg URI @@ -416,7 +416,7 @@ type Promise struct { ## 7.1 Support Types -``` ipldsch +``` haskell type CID = String type URI = String type Ability = String From 887bae335aa8bb3ac1ebf80c90ac415a41403809 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 20:31:11 -0800 Subject: [PATCH 035/154] add dataflow exaple --- README.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 532c39e2..e5247f90 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ The invocation wrapper MUST be signed by the same principal that issued the UCAN ### 3.1.1 Invocation -The `ucan/invoke` field MUST contain CIDs pointing to the UCANs to invoke. The outmost UCAN being invoked MUST NOT contain any actions that are not intended to be executed. +The `ucan/invoke` field MUST contain CIDs pointing to the UCANs to invoke. The outmost UCAN being invoked SHOULD NOT contain any actions that are not intended to be executed. ### 3.1.2 Version @@ -150,8 +150,6 @@ type Scope enum { ## 3.3 JSON Examples -### 3.3.1 Simple - ``` json { "ucan/invoke": "QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", @@ -162,7 +160,36 @@ type Scope enum { } ``` -### 3.3.2 Batched Pipeline +### 3.3.2 Pipelines + +The following examples both express the following call graph: + +``` json + ┌────────────────────────────┐ + │ │ + │ dns://example.com?TYPE=TXT │ + │ crud/update │ + │ │ + └───────┬──────────┬─────────┘ + │ │ + │ │ +┌────────────────────────▼───┐ ┌───▼────────────────────────┐ +│ │ │ │ +│ https://example.com/report │ │ mailto://alice@example.com │ +│ crud/update │ │ msg/send │ +│ │ │ │ +└────────────────────────┬───┘ └───┬────────────────────────┘ + │ │ + │ │ + ┌────────▼──────────▼────────┐ + │ │ + │ https://example.com/events │ + │ crud.create │ + │ │ + └────────────────────────────┘ +``` + +#### 3.3.2.1 Batched ``` json { @@ -235,13 +262,13 @@ type Scope enum { ] } }, - "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" + "sig": "kQHtTruysx4S8SrvSjTwr6ttTLzc7dd7atANUYT-SRK6v_SX8bjHegWoDak2x6vTAZ6CcVKBt6JGpgnjABpsoL" } { - "ucan/invoke": "Qmd4trNUhgWwsBbSsYBEWJqgiHyLrnhZ8u1DJsWsEKeuuF", + "ucan/invoke": "QmbXdT8QQMJ55Lb6MGJqTmwNzuUnHsE18t7zXGWeq9rQcV", "v": "0.1.0", - "nnc": "abcdef", + "nnc": "12345", "ext": null, "run": { "http://example.com/report": { @@ -259,7 +286,31 @@ type Scope enum { ] } }, - "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT+SRK6v/SX8bjt+VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" + "sig": "XZRSmp5cHaXX6xWzSTxQqC95kQHtTruysx4S8SrvSjTwr6ttTLzc7dd7atANUQJXoWThUiVuCHWdMnQNQJgiJi" +} + +{ + "ucan/invoke": "QmY4jEVE35u8SHegWoDak2x6vTAZ6Cc4cpSD5LAUQ23W7L", + "v": "0.1.0", + "nnc": "02468", + "ext": null, + "run": { + "https://example.com/events": { + "crud/create": [ + { + "event": "update-dns", + "status": "done" + }, + { + "_": [ + {"ucan/promise": ["/", "mailto://alice@example.com", "msg/send", null]} + {"ucan/promise": ["/", "https://example.com", "crud/update", null]} + ] + } + ] + } + }, + "sig": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7" } ``` From d8bf865bcf902a5fd8deb355b293b4f27f98d321 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 20:31:48 -0800 Subject: [PATCH 036/154] haha yeah taht wasn't json --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e5247f90..ad97e2aa 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ type Scope enum { The following examples both express the following call graph: -``` json +``` ┌────────────────────────────┐ │ │ │ dns://example.com?TYPE=TXT │ From 1483fea81063858bd54272cdc4929ca39e77063b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 21:05:05 -0800 Subject: [PATCH 037/154] Renumbering and tighten up promise synatx --- README.md | 135 +++++++++++++++++++----------------------------------- 1 file changed, 48 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index ad97e2aa..5b858de8 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ However, there is clearly a distinction between passing a function and invoking Information about the scheduling, order, and pipelining of actions 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 action by the latest invoker. -As we shall see in the [discussion of promise pipelining](#6-promise-pipelining), asking an agent to perform a sequence of actions 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](#5-promise-pipelining), asking an agent to perform a sequence of actions 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). # 2 Roles @@ -112,9 +112,13 @@ The `v` field MUST contain the version of the invocation object schema. ### 3.1.3 Run Capabilities -The OPTIONAL `run` field MUST reference the actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](#6-promise-pipelining)) are intended to be run, then they MUST be treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or lesser scope. +The OPTIONAL `run` field MUST reference the actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](#5-promise-pipelining)) are intended to be run, then they MUST be treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or lesser scope. -The only difference for the attenuated case is that [promises](#6-promise-pipelining) MAY also be used as inputs to fields. +#### 3.1.3.1 Promises + +The only difference from general capabilities is that [promises](#5-promise-pipelining) MAY also be used as inputs to attenuated fields. + +If a capability input has the key `"_"` and the value is a promise, the input MUST be discarded and only used for determining sequencing actions. ### 3.1.4 Nonce @@ -162,7 +166,7 @@ type Scope enum { ### 3.3.2 Pipelines -The following examples both express the following call graph: +The following examples both express the following dataflow graph: ``` ┌────────────────────────────┐ @@ -184,7 +188,7 @@ The following examples both express the following call graph: ┌────────▼──────────▼────────┐ │ │ │ https://example.com/events │ - │ crud.create │ + │ crud/create │ │ │ └────────────────────────────┘ ``` @@ -233,8 +237,8 @@ The following examples both express the following call graph: }, { "_": [ - {"ucan/promise": ["/", "mailto://alice@example.com", "msg/send", null]} - {"ucan/promise": ["/", "https://example.com", "crud/update", null]} + {"ucan/promise": ["/", "mailto://alice@example.com", "msg/send", ""]} + {"ucan/promise": ["/", "https://example.com", "crud/update", ""]} ] } ] @@ -303,8 +307,8 @@ The following examples both express the following call graph: }, { "_": [ - {"ucan/promise": ["/", "mailto://alice@example.com", "msg/send", null]} - {"ucan/promise": ["/", "https://example.com", "crud/update", null]} + {"ucan/promise": ["/", "mailto://alice@example.com", "msg/send", ""]} + {"ucan/promise": ["/", "https://example.com", "crud/update", ""]} ] } ] @@ -314,50 +318,24 @@ The following examples both express the following call graph: } ``` -# 4 Isolated Capabilities - -It is often important to be able to reference a specific capability in isolation, disentangling it from the its sibling capabilities and other configuration. This is important for being able to reference a specific Capability in Results, promise pipelining, logging, and for building external caches. - -| Field | Type | Description | Required | -|-------|----------|--------------------------------------------------------|----------| -| `res` | `URI` | The (canonicalized) URI of the resource being accessed | Yes | -| `aby` | `String` | The lowercase Ability called on the resource | Yes | -| `ins` | `[Any]` | Any other inputs required for the call | Yes | - -``` haskell -type Capability struct { - rsc URI -- Resource - aby Ability -- Ability - ins [Any] -- Inputs -} -``` - -``` json -{ - "rsc": "dns://_dnslink.example.com?TYPE=TXT", - "aby": "crud/update", - "ins": [{"value": "QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f"}] -} -``` - -# 5 Receipt +# 4 Receipt -An invocation receipt is an attested result about the output of an invocation. A receipt MUST be attested via signature of the principal (the `aud` of the associated UCAN). +An invocation receipt is an attestation of the output of an invocation. A receipt MUST be signed by the executor (the `aud` of the associated UCAN). Note that this does not guarantee correctness of the result! The statement's veracity MUST be only understood as an attestation from the executor. -## 5.1 Fields +## 4.1 Fields -| Field | Type | Description | Required | Default | -|----------------|--------------|----------------------------------------------------------------------------------|----------|---------| -| `ucan/receipt` | `CID` | CID of the Invocation that generated this response | Yes | | -| `rlt` | `{CID: Any}` | The results of each call, indexed by the CID of the [isolated capability](#4-isolated-capabilities) | Yes | | -| `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | -| `nnc` | `String` | A unique nonce, to distinguish each receipt | Yes | | -| `ext` | `Any` | Non-normative extended fields | No | `null` | -| `sig` | `Bytes` | Signature of the rest of the field canonicalized | Yes | | +| Field | Type | Description | Required | Default | +|----------------|--------------|------------------------------------------------------------------------------------------------------------------------------------|----------|---------| +| `ucan/receipt` | `CID` | CID of the Invocation that generated this response | Yes | | +| `rlt` | `{CID: Any}` | The results of each call, indexed by the CID of the [canonicalized capability](https://github.com/ucan-wg/ucan-ipld#22-capability) | Yes | | +| `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | +| `nnc` | `String` | A unique nonce, to distinguish each receipt | Yes | | +| `ext` | `Any` | Non-normative extended fields | No | `null` | +| `sig` | `Bytes` | Signature of the rest of the field canonicalized | Yes | | -## 5.1 IPLD +## 4.1 IPLD ``` haskell type Receipt struct { @@ -370,7 +348,7 @@ type Receipt struct { } ``` -## 5.2 JSON Example +## 4.2 JSON Example ``` json { @@ -404,8 +382,7 @@ type Receipt struct { } ``` - -# 6 Promise Pipelining +# 5 Promise Pipelining > 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 > @@ -415,57 +392,41 @@ At UCAN creation time, the UCAN MAY not yet have all of the information required The authority to execute later actions often cannot be fully attenuated in advance, since the executor controls the reported output of the prior step in a pipeline. When choosing to use pipelining, the invoker MUST delegate capabilities for any of the possible outputs. If tight control is required to limit authority, pipelining SHOULD NOT be used. -A promise MUST contain the CID of the target invocation, and the path of the - -because the resource may have a path in it, the resource needs to be broken out! - -Inverts the version from the outer invocation - -## 6.1 Fields - -| Field | Type | Description | Required | -|-----------------|-----------|---------------------------------|----------| -| `ucan/promised` | `CID` | The invocation being referenced | Yes | -| `using` | `URI` | The resource called | Yes | -| `called` | `Ability` | The ability used | Yes | -| `path` | `Path` | Path to the specific output | Yes | - -the know the concrete value required to scope the resource down sufficiently. This MAY be caused either by invoking them both in the same payload, or following one after another by CID reference. - -Variables relative to the result of some other action MAY be used. In this case, the attested (signed) receipt of the previous action MUST be included in the following form: +## 5.1 Fields -Referenced by invocation CID +| Field | Type | Description | Required | +|-----------------|--------------|---------------------------------|----------| +| `ucan/promised` | `CID or "/"` | The Invocation being referenced | Yes | +| `using` | `URI` | The resource called | Yes | +| `called` | `Ability` | The ability used | Yes | +| `path` | `Path` | Path to the specific output | No | +The above table MUST be serialzied as a tuple. In JSON, this SHOULD be represented as an array containing the values (but not keys) sequenced in the order they appear in the table. -## 6.2 IPLD Schema +## 5.2 IPLD Schema ``` haskell type Promise struct { pse &Invocation usg URI cll Ability -- output path - pth Path + pth optional Path } representation tuple ``` -## 6.3 JSON Example +## 5.3 JSON Examples -``` json -// IPLD -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/put", "http/statusCode"] - -// Full struct representation -{ - "ucan/promise": "QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", - "using": "example.com/foo/bar", - "called": "http/put", - "path": "http/status" -} +``` js +// All outputs +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/get"] + +// Only the status code +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/put", "statusCode"] ``` -# 7 Appendix +# 6 Appendix -## 7.1 Support Types +## 6.1 Support Types ``` haskell type CID = String @@ -489,7 +450,7 @@ type NumVer struct { } ``` -# 8 Prior Art +# 7 Prior Art [UCANTO](https://github.com/web3-storage/ucanto) from DAG House @@ -501,7 +462,7 @@ type NumVer struct { [Spritely Goblins](https://spritely.institute/static/papers/spritely-core.html) -# 9 Acknowledgements +# 8 Acknowledgements Thanks to the [DAG House](https://dag.house) team for initially suggesting UCAN as a generalized RPC framework. From 59a88d682a92a72437c61e2a839d6178c5b250d7 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 21:28:44 -0800 Subject: [PATCH 038/154] Prior Art --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5b858de8..8ab917ed 100644 --- a/README.md +++ b/README.md @@ -452,15 +452,15 @@ type NumVer struct { # 7 Prior Art -[UCANTO](https://github.com/web3-storage/ucanto) from DAG House +[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. -[CapTP](http://erights.org/elib/distrib/captp/index.html) +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. -[OCapN](https://github.com/ocapn/ocapn) +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. -[Cap'N Proto](https://capnproto.org/) +[Electronic Rights Transfer Protocol (ERTP)](https://docs.agoric.com/guides/ertp/) builds on top of CapTP for blockchain & digital asset use cases. -[Spritely Goblins](https://spritely.institute/static/papers/spritely-core.html) +[Cap'n Proto RPC](https://capnproto.org/) is an influential RPC framework [based on concepts from CapTP](https://capnproto.org/rpc.html#specification). # 8 Acknowledgements From a2a8813420e39f9f9c09580831c188b9a851eb4a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 21:31:14 -0800 Subject: [PATCH 039/154] Update dictionary --- .github/workflows/words-to-ignore.txt | 5 +++++ README.md | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 29074a18..7e27ebac 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -1,8 +1,11 @@ Acknowledgements +Agoric Akiko CIDs Cap'N +Cap'n CapTP +ERTP Gozalishvili Hardt Holmgren @@ -29,6 +32,7 @@ auth backend base64url canonicalized +dataflow de delegator dholms @@ -46,6 +50,7 @@ requestor's responder signalling trustless +ucanto unpadded url v0 diff --git a/README.md b/README.md index 8ab917ed..cff2110c 100644 --- a/README.md +++ b/README.md @@ -401,7 +401,7 @@ The authority to execute later actions often cannot be fully attenuated in advan | `called` | `Ability` | The ability used | Yes | | `path` | `Path` | Path to the specific output | No | -The above table MUST be serialzied as a tuple. In JSON, this SHOULD be represented as an array containing the values (but not keys) sequenced in the order they appear in the table. +The above table MUST be serialized as a tuple. In JSON, this SHOULD be represented as an array containing the values (but not keys) sequenced in the order they appear in the table. ## 5.2 IPLD Schema From f8e650e3e3046295b4853381905fdfc6357f7c41 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 21:32:28 -0800 Subject: [PATCH 040/154] Uh oh the dictionary may not understadn Cap'n --- .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 7e27ebac..0afc9496 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -4,6 +4,7 @@ Akiko CIDs Cap'N Cap'n +"Cap'n" CapTP ERTP Gozalishvili From 0fa485cbc4474ce1991565cce473a693f77fffdd Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 21:33:33 -0800 Subject: [PATCH 041/154] Let's thy this? --- .github/workflows/words-to-ignore.txt | 1 - README.md | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 0afc9496..7e27ebac 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -4,7 +4,6 @@ Akiko CIDs Cap'N Cap'n -"Cap'n" CapTP ERTP Gozalishvili diff --git a/README.md b/README.md index cff2110c..1aa255d4 100644 --- a/README.md +++ b/README.md @@ -460,7 +460,7 @@ The [Object Capability Network (OCapN)](https://github.com/ocapn/ocapn) protocol [Electronic Rights Transfer Protocol (ERTP)](https://docs.agoric.com/guides/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](https://capnproto.org/) is an influential RPC framework [based on concepts from CapTP](https://capnproto.org/rpc.html#specification). # 8 Acknowledgements From 1ef2a3293084d30f60e10471a4525b68cdfa9393 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 21:39:46 -0800 Subject: [PATCH 042/154] Consistency --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1aa255d4..d2baa77c 100644 --- a/README.md +++ b/README.md @@ -335,7 +335,7 @@ Note that this does not guarantee correctness of the result! The statement's ver | `ext` | `Any` | Non-normative extended fields | No | `null` | | `sig` | `Bytes` | Signature of the rest of the field canonicalized | Yes | | -## 4.1 IPLD +## 4.1 IPLD Schema ``` haskell type Receipt struct { From b1a38c6d906273e8a10a92f6db3ff8b87ab5d7c8 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 21:44:07 -0800 Subject: [PATCH 043/154] More complex example --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d2baa77c..87355ba9 100644 --- a/README.md +++ b/README.md @@ -421,7 +421,7 @@ type Promise struct { ["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/get"] // Only the status code -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/put", "statusCode"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/put", "payload/boris/company/0/name"] ``` # 6 Appendix @@ -464,10 +464,8 @@ The [Object Capability Network (OCapN)](https://github.com/ocapn/ocapn) protocol # 8 Acknowledgements -Thanks to the [DAG House](https://dag.house) team for initially suggesting UCAN as a generalized RPC framework. - 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/). 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 [Quinn Wilton](https://github.com/QuinnWilton) and [Marc-Antoine Parent](https://github.com/maparent) for their discussion of the distinction between declarations and directives. +Thanks to [Quinn Wilton](https://github.com/QuinnWilton) and [Marc-Antoine Parent](https://github.com/maparent) for their discussions of the distinction between declarations and directives both in and out of a UCAN context. From b21b5ce73d01d1a5f2b4eed275741c2452b4c00a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 21:45:03 -0800 Subject: [PATCH 044/154] Change syntax --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 87355ba9..1e1a8940 100644 --- a/README.md +++ b/README.md @@ -421,7 +421,7 @@ type Promise struct { ["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/get"] // Only the status code -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/put", "payload/boris/company/0/name"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/put", "payload.boris.company[0].name"] ``` # 6 Appendix From b6a2828cb7a8f34d33f7c66682773941de8f34e7 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 21:54:35 -0800 Subject: [PATCH 045/154] JSON Path (have I gone too far?!) --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 1e1a8940..f6c27755 100644 --- a/README.md +++ b/README.md @@ -394,12 +394,12 @@ The authority to execute later actions often cannot be fully attenuated in advan ## 5.1 Fields -| Field | Type | Description | Required | -|-----------------|--------------|---------------------------------|----------| -| `ucan/promised` | `CID or "/"` | The Invocation being referenced | Yes | -| `using` | `URI` | The resource called | Yes | -| `called` | `Ability` | The ability used | Yes | -| `path` | `Path` | Path to the specific output | No | +| Field | Type | Description | Required | +|-----------------|--------------|----------------------------------------------------------------------------------------------------|----------| +| `ucan/promised` | `CID or "/"` | The Invocation being referenced | Yes | +| `using` | `URI` | The URI of the resource called | Yes | +| `called` | `Ability` | The Ability used | Yes | +| `selector` | `String` | The [JSON Path](https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/) to the specific output | No | The above table MUST be serialized as a tuple. In JSON, this SHOULD be represented as an array containing the values (but not keys) sequenced in the order they appear in the table. @@ -410,7 +410,7 @@ type Promise struct { pse &Invocation usg URI cll Ability -- output path - pth optional Path + sel optional String } representation tuple ``` @@ -421,7 +421,7 @@ type Promise struct { ["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/get"] // Only the status code -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/put", "payload.boris.company[0].name"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/put", "$payload.users[*].company[:1].name"] ``` # 6 Appendix From cbcbea56090fc59e54f7720f170d05f23c775bc9 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 22:17:29 -0800 Subject: [PATCH 046/154] Switch to JSON Pointer for siplicity --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f6c27755..b1e7637f 100644 --- a/README.md +++ b/README.md @@ -394,12 +394,12 @@ The authority to execute later actions often cannot be fully attenuated in advan ## 5.1 Fields -| Field | Type | Description | Required | -|-----------------|--------------|----------------------------------------------------------------------------------------------------|----------| -| `ucan/promised` | `CID or "/"` | The Invocation being referenced | Yes | -| `using` | `URI` | The URI of the resource called | Yes | -| `called` | `Ability` | The Ability used | Yes | -| `selector` | `String` | The [JSON Path](https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/) to the specific output | No | +| Field | Type | Description | Required | +|-----------------|--------------|------------------------------------------------------------------------------------------------|----------| +| `ucan/promised` | `CID or "/"` | The Invocation being referenced | Yes | +| `using` | `URI` | The URI of the resource called | Yes | +| `called` | `Ability` | The Ability used | Yes | +| `selector` | `String` | The [RFC6901 JSON Pointer](https://www.rfc-editor.org/rfc/rfc6901.html) to the specific output | No | The above table MUST be serialized as a tuple. In JSON, this SHOULD be represented as an array containing the values (but not keys) sequenced in the order they appear in the table. @@ -421,7 +421,7 @@ type Promise struct { ["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/get"] // Only the status code -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/put", "$payload.users[*].company[:1].name"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/put", "/payload/users/0/employer/name"] ``` # 6 Appendix From 4ce2998456841fd626bca90e7b3fd0c643470514 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 22:20:33 -0800 Subject: [PATCH 047/154] An abstract would be nice --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b1e7637f..12d3c5bb 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ # 0 Abstract +UCAN Invocation defines a format for expressing the intention to run delegated capabilities from a UCAN, and how to promise pipeline invocations. + ## 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). From fdb39f3b1022978ba48b12cef79e83a8e01b288e Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 22:24:53 -0800 Subject: [PATCH 048/154] Numbering --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 12d3c5bb..75b83a6a 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Delegating a capability is like the statement `message`. Invocation is akin to ` 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.3 Separation of Concerns +## 1.2 Separation of Concerns Information about the scheduling, order, and pipelining of actions 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 action by the latest invoker. From 1236f4f41aebfe171d595dd3ce45f40c7171cade Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 26 Nov 2022 22:26:00 -0800 Subject: [PATCH 049/154] ...more numbering --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 75b83a6a..322b8706 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,8 @@ Invocation adds two new roles to UCAN: invoker and executor. The existing UCAN d | `iss` | Delegator: transfer authority (active) | Invoker: request action (active) | | `aud` | Delegate: gain authority (passive) | Executor: perform action (active) | +## 2.1 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. From b542457e45c1c4be32fcb038a1b7a98271ec7ca3 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 27 Nov 2022 13:54:40 -0800 Subject: [PATCH 050/154] Update diagram and (somewhat annoyingly) calling convention --- README.md | 224 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 147 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 322b8706..3dff1dd9 100644 --- a/README.md +++ b/README.md @@ -97,14 +97,14 @@ The invocation wrapper MUST be signed by the same principal that issued the UCAN ## 3.1 Fields -| Field | Type | Value | Description | Required | Default | -|---------------|------------------------------------|-----------|--------------------------------------------------|----------|---------| -| `ucan/invoke` | `CID` | | The CID of the UCAN to invoke | Yes | | -| `v` | `SemVer` | `"0.1.0"` | SemVer of the UCAN invocation object schema | Yes | | -| `run` | `"*" or {URI : {Ability : [Any]}}` | | Which UCAN capabilities to run | No | `"*"` | -| `nnc` | `String` | | A unique nonce, to distinguish each invocation | Yes | | -| `ext` | `Any` | | Non-normative extended fields | No | `null` | -| `sig` | `Bytes` | | Signature of the rest of the field canonicalized | Yes | | +| Field | Type | Value | Description | Required | Default | +|---------------|----------------------------------------------|-----------|--------------------------------------------------|----------|---------| +| `ucan/invoke` | `CID` | | The CID of the UCAN to invoke | Yes | | +| `v` | `SemVer` | `"0.1.0"` | SemVer of the UCAN invocation this schema | Yes | | +| `run` | `"*" or {String: {URI : {Ability : [Any]}}}` | | Which UCAN capabilities to run | No | `"*"` | +| `nnc` | `String` | | A unique nonce, to distinguish each invocation | Yes | | +| `ext` | `Any` | | Non-normative extended fields | No | `null` | +| `sig` | `Bytes` | | Signature of the rest of the field canonicalized | Yes | | ### 3.1.1 Invocation @@ -116,7 +116,7 @@ The `v` field MUST contain the version of the invocation object schema. ### 3.1.3 Run Capabilities -The OPTIONAL `run` field MUST reference the actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](#5-promise-pipelining)) are intended to be run, then they MUST be treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or lesser scope. +The OPTIONAL `run` field MUST reference the actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](#5-promise-pipelining)) are intended to be run, then they MUST be prefixed with an arbitrary label and treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or lesser scope. #### 3.1.3.1 Promises @@ -158,11 +158,24 @@ type Scope enum { ## 3.3 JSON Examples - ``` json +### 3.3.1 Run All + + ``` js +{ + "ucan/invoke": "QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", + "v": "0.1.0", + "nnc": "abcdef", + "ext": null, + "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" +} +``` + + ``` js { "ucan/invoke": "QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "v": "0.1.0", "nnc": "abcdef", + "run": "*", // Exmplicitely "run all" "ext": null, "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" } @@ -173,28 +186,29 @@ type Scope enum { The following examples both express the following dataflow graph: ``` - ┌────────────────────────────┐ - │ │ - │ dns://example.com?TYPE=TXT │ - │ crud/update │ - │ │ - └───────┬──────────┬─────────┘ - │ │ - │ │ -┌────────────────────────▼───┐ ┌───▼────────────────────────┐ -│ │ │ │ -│ https://example.com/report │ │ mailto://alice@example.com │ -│ crud/update │ │ msg/send │ -│ │ │ │ -└────────────────────────┬───┘ └───┬────────────────────────┘ - │ │ - │ │ - ┌────────▼──────────▼────────┐ - │ │ - │ https://example.com/events │ - │ crud/create │ - │ │ - └────────────────────────────┘ + ┌────────────────────────────┐ + │ │ + │ dns://example.com?TYPE=TXT │ + │ crud/update │ + │ │ + └───────┬──────────┬─────────┘ + │ │ + │ │ +┌─────────────────────▼───┐ ┌───▼────────────────────────┐ +│ │ │ │ +│ mailto:alice@exaple.com │ │ mailto://alice@example.com │ +│ msg/send │ │ msg/send │ +│ bob@example.com │ │ carol@exmaple.com │ +│ │ │ │ +└─────────────────────┬───┘ └───┬────────────────────────┘ + │ │ + │ │ + ┌────────▼──────────▼────────┐ + │ │ + │ https://example.com/events │ + │ crud/create │ + │ │ + └────────────────────────────┘ ``` #### 3.3.2.1 Batched @@ -206,44 +220,50 @@ The following examples both express the following dataflow graph: "nnc": "abcdef", "ext": null, "run": { - "dns://example.com?TYPE=TXT": { - "crud/update": [ - { - "value": "hello world", - "retries": 5 - } - ] + "update-dns" : { + "using": "dns://example.com?TYPE=TXT": + "do": "crud/update", + "inputs": { + "value": "hello world", + "retries": 5 + } }, - "https://example.com/report": { - "crud/update": [ + "notify-bob": { + "using": "mailto://alice@example.com", + "do": "msg/send", + "inputs": [ { - "performedBy": "did:key:zAlice", - "tags": ["hello", "world"], - "status": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/statusCode"]}, - "payload": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} + "to": "bob@example.com", + "subject": "DNSLink for example.com", + "body": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} } ] }, - "mailto://alice@example.com": { - "msg/send": [ + "notify-carol": { + "using": "mailto://alice@example.com", + "do": "msg/send", + "inputs": [ { - "to": "bob@example.com", + "to": "carol@example.com", "subject": "DNSLink for example.com", "body": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} } ] }, - "https://example.com/events": { - "crud/create": [ - { - "event": "update-dns", - "status": "done" + "log-as-done": { + "using": "https://example.com/report" + "do": "crud/update" + "inputs": [ + { + "from": "mailto://alice@exmaple.com", + "to": ["bob@exmaple.com", "carol@example.com"], + "event": "email-notification", + }, + { + "_": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} }, { - "_": [ - {"ucan/promise": ["/", "mailto://alice@example.com", "msg/send", ""]} - {"ucan/promise": ["/", "https://example.com", "crud/update", ""]} - ] + "_": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} } ] } @@ -254,6 +274,43 @@ The following examples both express the following dataflow graph: ### 3.3.3 Serial Pipeline +``` + ┌────────────────────────────┐ + │ │ + │ dns://example.com?TYPE=TXT │ + │ crud/update │ + │ │ + └───────┬──────────┬─────────┘ + │ │ +┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ + │ │ + │ ┌───▼────────────────────────┐ + │ │ │ + │ │ mailto://alice@example.com │ + │ │ msg/send │ + │ │ carol@exmaple.com │ + │ │ │ + │ └───┬────────────────────────┘ + │ │ +┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ + │ │ +┌─────────────────────▼───┐ │ +│ │ │ +│ mailto:alice@exaple.com │ │ +│ msg/send │ │ +│ bob@example.com │ │ +│ │ │ +└─────────────────────┬───┘ │ + │ │ + │ │ + ┌────────▼──────────▼────────┐ + │ │ + │ https://example.com/events │ + │ crud/create │ + │ │ + └────────────────────────────┘ +``` + ``` json { "ucan/invoke": "Qmd4trNUhgWwsBbSsYBEWJqgiHyLrnhZ8u1DJsWsEKeuuF", @@ -261,8 +318,10 @@ The following examples both express the following dataflow graph: "nnc": "abcdef", "ext": null, "run": { - "dns://example.com?TYPE=TXT": { - "crud/update": [ + "update-dns": { + "using": "dns://example.com?TYPE=TXT", + "do": "crud/update" + "inputs": [ { "value": "hello world", "retries": 5 @@ -279,17 +338,14 @@ The following examples both express the following dataflow graph: "nnc": "12345", "ext": null, "run": { - "http://example.com/report": { - "http/post": [ - {"updateTo": {"ucan/promise": ["QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", "dns://example.com?TYPE=TXT", "crud/update", "http/0/statusCode"]}} - ] - }, - "mailto://alice@example.com": { - "msg/send": [ + "notify-carol": { + "using": "mailto://alice@example.com", + "do": "msg/send", + "inputs": [ { - "to": "bob@example.com", + "to": "carol@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", "dns://example.com?TYPE=TXT", "crud/update", "http/0/body"]} + "body": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} } ] } @@ -303,17 +359,31 @@ The following examples both express the following dataflow graph: "nnc": "02468", "ext": null, "run": { - "https://example.com/events": { - "crud/create": [ - { - "event": "update-dns", - "status": "done" + "notify-bob": { + "using": "mailto://alice@example.com", + "do": "msg/send", + "inputs": [ + { + "to": "bob@example.com", + "subject": "DNSLink for example.com", + "body": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} + } + ] + }, + "log-as-done": { + "using": "https://example.com/report" + "do": "crud/update" + "inputs": [ + { + "from": "mailto://alice@exmaple.com", + "to": ["bob@exmaple.com", "carol@example.com"], + "event": "email-notification", + }, + { + "_": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} }, { - "_": [ - {"ucan/promise": ["/", "mailto://alice@example.com", "msg/send", ""]} - {"ucan/promise": ["/", "https://example.com", "crud/update", ""]} - ] + "_": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} } ] } From f935c5d9e92b984a51e0a96e2ee24672b7080375 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 27 Nov 2022 13:57:46 -0800 Subject: [PATCH 051/154] Better? --- README.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/README.md b/README.md index 3dff1dd9..7eeb17b6 100644 --- a/README.md +++ b/README.md @@ -309,6 +309,49 @@ The following examples both express the following dataflow graph: │ crud/create │ │ │ └────────────────────────────┘ + + + + + + + + ┌────────────────────────────┐ + │ │ + │ dns://example.com?TYPE=TXT │ + │ crud/update │ + │ │ + └───────┬──────────┬─────────┘ + │ │ + │ │ + │ │ + │ ┌───▼────────────────────────┐ + │ │ │ + │ │ mailto://alice@example.com │ + │ │ msg/send │ + │ │ carol@exmaple.com │ + │ │ │ + │ └───┬────────────────────────┘ + │ │ +┌───────────────────────┼──────────┼──────────┐ +│ │ │ │ +│ ┌─────────────────────▼───┐ │ │ +│ │ │ │ │ +│ │ mailto:alice@exaple.com │ │ │ +│ │ msg/send │ │ │ +│ │ bob@example.com │ │ │ +│ │ │ │ │ +│ └─────────────────────┬───┘ │ │ +│ │ │ │ +│ │ │ │ +│ ┌────────▼──────────▼────────┐ │ +│ │ │ │ +│ │ https://example.com/events │ │ +│ │ crud/create │ │ +│ │ │ │ +│ └────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────┘ ``` ``` json From ca95eb28b3dbf62ae6628b6cb646723599f1b9c0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 27 Nov 2022 14:08:13 -0800 Subject: [PATCH 052/154] Update receipt --- README.md | 65 +++++++++++++------------------------------------------ 1 file changed, 15 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 7eeb17b6..825a3369 100644 --- a/README.md +++ b/README.md @@ -152,8 +152,14 @@ type Invocation struct { type Scope enum { | All ("*") - | {URI : {Ability : [Any]}} + | Action } + +type Action enum { + using URI + do Ability + inputs Any +} ``` ## 3.3 JSON Examples @@ -275,47 +281,6 @@ The following examples both express the following dataflow graph: ### 3.3.3 Serial Pipeline ``` - ┌────────────────────────────┐ - │ │ - │ dns://example.com?TYPE=TXT │ - │ crud/update │ - │ │ - └───────┬──────────┬─────────┘ - │ │ -┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ - │ │ - │ ┌───▼────────────────────────┐ - │ │ │ - │ │ mailto://alice@example.com │ - │ │ msg/send │ - │ │ carol@exmaple.com │ - │ │ │ - │ └───┬────────────────────────┘ - │ │ -┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ - │ │ -┌─────────────────────▼───┐ │ -│ │ │ -│ mailto:alice@exaple.com │ │ -│ msg/send │ │ -│ bob@example.com │ │ -│ │ │ -└─────────────────────┬───┘ │ - │ │ - │ │ - ┌────────▼──────────▼────────┐ - │ │ - │ https://example.com/events │ - │ crud/create │ - │ │ - └────────────────────────────┘ - - - - - - - ┌────────────────────────────┐ │ │ │ dns://example.com?TYPE=TXT │ @@ -443,14 +408,14 @@ Note that this does not guarantee correctness of the result! The statement's ver ## 4.1 Fields -| Field | Type | Description | Required | Default | -|----------------|--------------|------------------------------------------------------------------------------------------------------------------------------------|----------|---------| -| `ucan/receipt` | `CID` | CID of the Invocation that generated this response | Yes | | -| `rlt` | `{CID: Any}` | The results of each call, indexed by the CID of the [canonicalized capability](https://github.com/ucan-wg/ucan-ipld#22-capability) | Yes | | -| `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | -| `nnc` | `String` | A unique nonce, to distinguish each receipt | Yes | | -| `ext` | `Any` | Non-normative extended fields | No | `null` | -| `sig` | `Bytes` | Signature of the rest of the field canonicalized | Yes | | +| Field | Type | Description | Required | Default | +|----------------|--------------|--------------------------------------------------------------------------------------------------|----------|---------| +| `ucan/receipt` | `CID` | CID of the Invocation that generated this response | Yes | | +| `rlt` | `{CID: Any}` | The results of each call, indexed by the CID of the `dag-cbor` encoded [Action](#32-ipld-schema) | Yes | | +| `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | +| `nnc` | `String` | A unique nonce, to distinguish each receipt | Yes | | +| `ext` | `Any` | Non-normative extended fields | No | `null` | +| `sig` | `Bytes` | Signature of the rest of the field canonicalized | Yes | | ## 4.1 IPLD Schema From 102cd4143ae8523f24c4134b80a7be2061c5ae20 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 27 Nov 2022 15:14:27 -0800 Subject: [PATCH 053/154] Add invocation flow diagram --- README.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/README.md b/README.md index 825a3369..d36ad899 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,52 @@ Information about the scheduling, order, and pipelining of actions is orthogonal As we shall see in the [discussion of promise pipelining](#5-promise-pipelining), asking an agent to perform a sequence of actions 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). +``` + ────────────────────────────────────────────Time──────────────────────────────────────────────────────► + +┌──────────────────────────────────────────Delegation─────────────────────────────────────────────────────┐ +│ │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ +│ │ │ │ │ │ │ │ │ │ │ │ +│ │ Alice ├──►│ Bob ├──►│ Carol ├────────►│ Dan ├───────────────►│ Erin │ │ +│ │ │ │ │ │ │ │ │ │ │ │ +│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + +┌──────────────────────────────────────────Invocation─────────────────────────────────────────────────────┐ +│ │ +│ ┌─────────┐ ┌─────────┐ │ +│ │ │ │ │ │ +│ │ Carol ╞═══All══►│ Dan │ │ +│ │ │ │ │ │ +│ └─────────┘ └─────────┘ │ +│ │ +│ ┌─────────┐ ┌─────────┐ │ +│ │ │ │ │ │ +│ │ Dan ╞══Read Email══►│ Erin │ │ +│ │ │ │ │ │ +│ └─────────┘ └─────────┘ │ +│ │ +│ ┌─────────┐ ┌─────────┐ │ +│ │ │ │ │ │ +│ │ Dan ╞═══Read Email══►│ Erin │ │ +│ │ │ ▲ │ │ │ +│ └─────────┘ ┆ └─────────┘ │ +│ ┆ │ +│ Using │ +│ Result │ +│ Of │ +│ ┆ │ +│ ┌─────────┐ ┆ ┌─────────┐ │ +│ │ │ ┆ │ │ │ +│ │ Dan ╞════Set DNS══►│ Erin │ │ +│ │ │ │ │ │ +│ └─────────┘ └─────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +``` + # 2 Roles Invocation adds two new roles to UCAN: invoker and executor. The existing UCAN delegator and delegate principals MUST persist to the invocation. From da1ba1567c6baefe2e23e6eeebbdc75181806416 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 27 Nov 2022 15:17:09 -0800 Subject: [PATCH 054/154] Move elements in diagram --- README.md | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d36ad899..67e0e5d7 100644 --- a/README.md +++ b/README.md @@ -93,18 +93,15 @@ As we shall see in the [discussion of promise pipelining](#5-promise-pipelining) │ │ │ │ │ │ │ └─────────┘ └─────────┘ │ │ │ -│ ┌─────────┐ ┌─────────┐ │ -│ │ │ │ │ │ -│ │ Dan ╞═══Read Email══►│ Erin │ │ -│ │ │ ▲ │ │ │ -│ └─────────┘ ┆ └─────────┘ │ -│ ┆ │ -│ Using │ -│ Result │ -│ Of │ -│ ┆ │ -│ ┌─────────┐ ┆ ┌─────────┐ │ -│ │ │ ┆ │ │ │ +│ ┌─────────┐ ┌─────────┐ │ +│ │ │ │ │ │ +│ │ Dan ╞═══Read Email══►│ Erin │ │ +│ │ │ ▲ │ │ │ +│ └─────────┘ ┆ └─────────┘ │ +│ Using │ +│ Result │ +│ ┌─────────┐ Of ┌─────────┐ │ +│ │ │ ┆ │ │ │ │ │ Dan ╞════Set DNS══►│ Erin │ │ │ │ │ │ │ │ │ └─────────┘ └─────────┘ │ From ae0f6fd348287dc68bf092ae847f7e409d8a71bf Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 27 Nov 2022 15:18:52 -0800 Subject: [PATCH 055/154] LOL more realistic for DSys --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 67e0e5d7..0bf72488 100644 --- a/README.md +++ b/README.md @@ -87,11 +87,11 @@ As we shall see in the [discussion of promise pipelining](#5-promise-pipelining) │ │ │ │ │ │ │ └─────────┘ └─────────┘ │ │ │ -│ ┌─────────┐ ┌─────────┐ │ -│ │ │ │ │ │ -│ │ Dan ╞══Read Email══►│ Erin │ │ -│ │ │ │ │ │ -│ └─────────┘ └─────────┘ │ +│ ┌─────────┐ ┌─────────┐ │ +│ │ │ │ │ │ +│ │ Dan ╞═══════════Read Email════════►│ Erin │ │ +│ │ │ │ │ │ +│ └─────────┘ └─────────┘ │ │ │ │ ┌─────────┐ ┌─────────┐ │ │ │ │ │ │ │ From c4b4f181be3669e32a735dc15d5e077aaf886956 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 27 Nov 2022 15:19:35 -0800 Subject: [PATCH 056/154] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0bf72488..82b67aec 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ As we shall see in the [discussion of promise pipelining](#5-promise-pipelining) │ │ │ ┌─────────┐ ┌─────────┐ │ │ │ │ │ │ │ -│ │ Dan ╞═══════════Read Email════════►│ Erin │ │ +│ │ Dan ╞═══════════Update DB═════════►│ Erin │ │ │ │ │ │ │ │ │ └─────────┘ └─────────┘ │ │ │ From fc9cf5576107003293124caa7c094781c1516b35 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 27 Nov 2022 15:55:11 -0800 Subject: [PATCH 057/154] Thank Bacalhau folks :) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 82b67aec..a43dedf8 100644 --- a/README.md +++ b/README.md @@ -594,3 +594,5 @@ Many thanks to [Mark Miller](https://github.com/erights) for his [pioneering wor 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 [Quinn Wilton](https://github.com/QuinnWilton) and [Marc-Antoine Parent](https://github.com/maparent) for their discussions of the distinction between declarations and directives both in and out of a UCAN context. + +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). From b9dbc6c6ac450c3017a6a6c90d8cec6309d01b13 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 27 Nov 2022 15:56:19 -0800 Subject: [PATCH 058/154] Update dictionary --- .github/workflows/words-to-ignore.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 7e27ebac..509f6988 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -1,6 +1,7 @@ Acknowledgements Agoric Akiko +Bacalhau CIDs Cap'N Cap'n @@ -10,11 +11,13 @@ Gozalishvili Hardt Holmgren IPLD +IPVM Irakli JSON JWT JWT-encoded Lemmer-Webber +Marsen OCapN Pre-Draft Proto @@ -27,6 +30,7 @@ UCAN's UCANTO UCANs URI +Worthington Zelenka auth backend From ed201f1ffeec9b235c1baf757bc03ddb4a71712b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 27 Nov 2022 18:20:07 -0800 Subject: [PATCH 059/154] Update examples to match text --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a43dedf8..cb525c82 100644 --- a/README.md +++ b/README.md @@ -284,7 +284,7 @@ The following examples both express the following dataflow graph: { "to": "bob@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} + "body": {"ucan/promise": ["/", "update-dns", "http/body"]} } ] }, @@ -295,7 +295,7 @@ The following examples both express the following dataflow graph: { "to": "carol@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} + "body": {"ucan/promise": ["/", "update-dns", "http/body"]} } ] }, @@ -309,10 +309,10 @@ The following examples both express the following dataflow graph: "event": "email-notification", }, { - "_": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} + "_": {"ucan/promise": ["/", "notify-bob", "http/body"]} }, { - "_": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} + "_": {"ucan/promise": ["/", "notify-carol", "http/body"]} } ] } @@ -396,7 +396,7 @@ The following examples both express the following dataflow graph: { "to": "carol@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} + "body": {"ucan/promise": ["QmcKi4Z1L7VH4HahyrXb8Rzjex5U7DiY12PnTnysxviuDT", "update-dns", http/body"]} } ] } @@ -417,7 +417,7 @@ The following examples both express the following dataflow graph: { "to": "bob@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} + "body": {"ucan/promise": ["QmcKi4Z1L7VH4HahyrXb8Rzjex5U7DiY12PnTnysxviuDT", "update-dns", http/body"]} } ] }, @@ -431,10 +431,10 @@ The following examples both express the following dataflow graph: "event": "email-notification", }, { - "_": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} + "_": {"ucan/promise": ["/", "notify-bob", http/body"]} }, { - "_": {"ucan/promise": ["/", "dns://example.com?TYPE=TXT", "crud/update", "http/body"]} + "_": {"ucan/promise": ["QmaCRSugy2sXEKxjDDwcqLx2Bs7CUGpZSrtsASsNyntyC9", "notify-carol", http/body"]} } ] } From fc1f1e7518b544e10bf78ebffe2a248c2b6e16a4 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 27 Nov 2022 18:47:04 -0800 Subject: [PATCH 060/154] Tighten up selectors --- README.md | 55 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index cb525c82..5798044f 100644 --- a/README.md +++ b/README.md @@ -284,7 +284,7 @@ The following examples both express the following dataflow graph: { "to": "bob@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "update-dns", "http/body"]} + "body": {"ucan/promise": ["/", "update-dns", "$http.body"]} } ] }, @@ -295,7 +295,7 @@ The following examples both express the following dataflow graph: { "to": "carol@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "update-dns", "http/body"]} + "body": {"ucan/promise": ["/", "update-dns", "$http.body"]} } ] }, @@ -309,10 +309,10 @@ The following examples both express the following dataflow graph: "event": "email-notification", }, { - "_": {"ucan/promise": ["/", "notify-bob", "http/body"]} + "_": {"ucan/promise": ["/", "notify-bob", "$http.body"]} }, { - "_": {"ucan/promise": ["/", "notify-carol", "http/body"]} + "_": {"ucan/promise": ["/", "notify-carol", "$http.body"]} } ] } @@ -396,7 +396,7 @@ The following examples both express the following dataflow graph: { "to": "carol@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["QmcKi4Z1L7VH4HahyrXb8Rzjex5U7DiY12PnTnysxviuDT", "update-dns", http/body"]} + "body": {"ucan/promise": ["QmcKi4Z1L7VH4HahyrXb8Rzjex5U7DiY12PnTnysxviuDT", "update-dns", "$http.body"]} } ] } @@ -417,7 +417,7 @@ The following examples both express the following dataflow graph: { "to": "bob@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["QmcKi4Z1L7VH4HahyrXb8Rzjex5U7DiY12PnTnysxviuDT", "update-dns", http/body"]} + "body": {"ucan/promise": ["QmcKi4Z1L7VH4HahyrXb8Rzjex5U7DiY12PnTnysxviuDT", "update-dns", $http.body"]} } ] }, @@ -431,10 +431,10 @@ The following examples both express the following dataflow graph: "event": "email-notification", }, { - "_": {"ucan/promise": ["/", "notify-bob", http/body"]} + "_": {"ucan/promise": ["/", "notify-bob", "$http.body"]} }, { - "_": {"ucan/promise": ["QmaCRSugy2sXEKxjDDwcqLx2Bs7CUGpZSrtsASsNyntyC9", "notify-carol", http/body"]} + "_": {"ucan/promise": ["QmaCRSugy2sXEKxjDDwcqLx2Bs7CUGpZSrtsASsNyntyC9", "notify-carol", "$http.body"]} } ] } @@ -519,12 +519,11 @@ The authority to execute later actions often cannot be fully attenuated in advan ## 5.1 Fields -| Field | Type | Description | Required | -|-----------------|--------------|------------------------------------------------------------------------------------------------|----------| -| `ucan/promised` | `CID or "/"` | The Invocation being referenced | Yes | -| `using` | `URI` | The URI of the resource called | Yes | -| `called` | `Ability` | The Ability used | Yes | -| `selector` | `String` | The [RFC6901 JSON Pointer](https://www.rfc-editor.org/rfc/rfc6901.html) to the specific output | No | +| Field | Type | Description | Required | Default | +|-----------------|--------------|---------------------------------------------------------------------------------------|----------|----------| +| `ucan/promised` | `CID or "/"` | The Invocation being referenced | Yes | | +| `label` | `String` | The action's label. If the actions were implicit (`"run": "*"`), the the CID is used | Yes | | +| `selector` | `String` | The [JSONPath](https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/) expression | No | `"$..*"` | The above table MUST be serialized as a tuple. In JSON, this SHOULD be represented as an array containing the values (but not keys) sequenced in the order they appear in the table. @@ -532,21 +531,35 @@ The above table MUST be serialized as a tuple. In JSON, this SHOULD be represent ``` haskell type Promise struct { - pse &Invocation - usg URI - cll Ability -- output path - sel optional String + promised Target + actionlabel String -- The label inside the invocation + jsonpath String (implicit "$..*") -- JSONPath Expression } representation tuple + +type Target enum { + | Local (rename "/") + | Rmeote(CID) +} ``` ## 5.3 JSON Examples ``` js // All outputs -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/get"] + +["/", "some-label"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "QmeURvKzGm85Ekt1t1wDJ3niHAXuhexi281N2ig311pLZv"] + +["/", "some-label", "$..*"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label", "$..*"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "QmeURvKzGm85Ekt1t1wDJ3niHAXuhexi281N2ig311pLZv", "$..*"] // Only the status code -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "example.com/foo/bar", "http/put", "/payload/users/0/employer/name"] +["/", "some-label" "$payload.users[0].employer.name"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label", "$payload.users[0].employer.name"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label", "$payload.users[0].employer.name"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "QmeURvKzGm85Ekt1t1wDJ3niHAXuhexi281N2ig311pLZv", "$payload.users[0].employer.name"] ``` # 6 Appendix @@ -561,7 +574,7 @@ type Path = String type SemVer struct { num NumVer - tag nullable optional String + tag String (implicit Null) } representation stringjoin { join "+" } From 6eb752d765550e6942231e781f8cc94a1c031671 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 27 Nov 2022 18:49:31 -0800 Subject: [PATCH 061/154] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5798044f..04ca72bd 100644 --- a/README.md +++ b/README.md @@ -417,7 +417,7 @@ The following examples both express the following dataflow graph: { "to": "bob@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["QmcKi4Z1L7VH4HahyrXb8Rzjex5U7DiY12PnTnysxviuDT", "update-dns", $http.body"]} + "body": {"ucan/promise": ["QmcKi4Z1L7VH4HahyrXb8Rzjex5U7DiY12PnTnysxviuDT", "update-dns", "$http.body"]} } ] }, From 7840f2567bec1fbb541e4cb05b678d5ed3bbd6b1 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 27 Nov 2022 18:50:32 -0800 Subject: [PATCH 062/154] Update IPLD Schema markdown highlighting --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 04ca72bd..59128e92 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,7 @@ If serialized as JSON, the `sig` field MUST be serialized as [unpadded base64url ## 3.2 IPLD Schema -``` haskell +``` ipldsch type Invocation struct { inv &UCAN -- The UCAN providing authority v SemVer -- Version @@ -462,7 +462,7 @@ Note that this does not guarantee correctness of the result! The statement's ver ## 4.1 IPLD Schema -``` haskell +``` ipldsch type Receipt struct { rec &Invocation rlt {URI : {Ability : Any}} @@ -529,7 +529,7 @@ The above table MUST be serialized as a tuple. In JSON, this SHOULD be represent ## 5.2 IPLD Schema -``` haskell +``` ipldsch type Promise struct { promised Target actionlabel String -- The label inside the invocation @@ -566,7 +566,7 @@ type Target enum { ## 6.1 Support Types -``` haskell +``` ipldsch type CID = String type URI = String type Ability = String From 3eacc1be711e8c004223521c3566a0c2d54f06e0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 27 Nov 2022 18:52:12 -0800 Subject: [PATCH 063/154] Update 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 509f6988..566a2635 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -14,6 +14,7 @@ IPLD IPVM Irakli JSON +JSONPath JWT JWT-encoded Lemmer-Webber From 5344b52116db6e36fad5b61879f80db921207ee0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 28 Nov 2022 00:35:03 -0800 Subject: [PATCH 064/154] Introduce promise pipelinig much later in the spec --- README.md | 303 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 175 insertions(+), 128 deletions(-) diff --git a/README.md b/README.md index 59128e92..8ae5138d 100644 --- a/README.md +++ b/README.md @@ -230,9 +230,167 @@ type Action enum { } ``` -### 3.3.2 Pipelines +### 3.3.2 Promise Pipelines -The following examples both express the following dataflow graph: +Promise pipelines are handled in more detail in [section 5](#5-promise-pipelining). In brief, they enable taking the output of an action that has not yet run as the input to another action. Here is a simple example: + +``` js +{ + "ucan/invoke": "QmY4jEVE35u8SHegWoDak2x6vTAZ6Cc4cpSD5LAUQ23W7L", + "v": "0.1.0", + "nnc": "02468", + "ext": null, + "run": { + "notify-bob": { + "using": "mailto://alice@example.com", + "do": "msg/send", + "inputs": [ + { + "to": "bob@example.com", + "subject": "DNSLink for example.com", + "body": "Hello Bob!" + } + ] + }, + "log-as-done": { + "using": "https://example.com/report" + "do": "crud/update" + "inputs": { + "from": "mailto://alice@exmaple.com", + "to": ["bob@exmaple.com"], + "event": "email-notification", + "value": {"ucan/promise": ["/", "notify-bob"]} // Pipelined promise + } + } + }, + "sig": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7" +} +``` + +# 4 Receipt + +An invocation receipt is an attestation of the output of an invocation. A receipt MUST be signed by the executor (the `aud` of the associated UCAN). + +Note that this does not guarantee correctness of the result! The statement's veracity MUST be only understood as an attestation from the executor. + +## 4.1 Fields + +| Field | Type | Description | Required | Default | +|----------------|--------------|--------------------------------------------------------------------------------------------------|----------|---------| +| `ucan/receipt` | `CID` | CID of the Invocation that generated this response | Yes | | +| `rlt` | `{CID: Any}` | The results of each call, indexed by the CID of the `dag-cbor` encoded [Action](#32-ipld-schema) | Yes | | +| `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | +| `nnc` | `String` | A unique nonce, to distinguish each receipt | Yes | | +| `ext` | `Any` | Non-normative extended fields | No | `null` | +| `sig` | `Bytes` | Signature of the rest of the field canonicalized | Yes | | + +## 4.1 IPLD Schema + +``` ipldsch +type Receipt struct { + rec &Invocation + rlt {URI : {Ability : Any}} + v SemVer + nnc String + ext optional Any + sig Bytes +} +``` + +## 4.2 JSON Example + +``` json +{ + "ucan/receipt": "QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", + "v": "0.1.0", + "rlt": { + "QmYqbJBxCqqzDouPMbcrbNuB3WHWajSyq4RWin7ufs2ajf": [ + { + "from": "alice@example.com", + "text": "Hello world!" + }, + { + "from": "bob@example.com", + "text": "What's up?" + } + ], + "QmXrfqKNUpRiyyi8r3hpSZyZuG3S2MY9rTw1p8iEC9FNh5": { + "http": { + "status": 200, + "body": "hello world" + }, + "ms": 476 + } + }, + "nnc": "xyz", + "ext": { + "notes": "very receipt. such wow.", + "tags": ["dag-house", "fission"] + }, + "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt_VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" + } +``` + +# 5 Promise Pipelining + +> 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 + +At UCAN creation time, the UCAN MAY not yet have all of the information required to construct the next request in a sequence. Waiting for each request to complete before proceeding to the next action 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 earlier invocations into the input of a later one. This liberates the invoker from waiting for each step. + +The authority to execute later actions often cannot be fully attenuated in advance, since the executor controls the reported output of the prior step in a pipeline. When choosing to use pipelining, the invoker MUST delegate capabilities for any of the possible outputs. If tight control is required to limit authority, pipelining SHOULD NOT be used. + +## 5.1 Promises + +## 5.1.1 Fields + +| Field | Type | Description | Required | Default | +|-----------------|--------------|---------------------------------------------------------------------------------------|----------|----------| +| `ucan/promised` | `CID or "/"` | The Invocation being referenced | Yes | | +| `label` | `String` | The action's label. If the actions were implicit (`"run": "*"`), the the CID is used | Yes | | +| `selector` | `String` | The [JSONPath](https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/) expression | No | `"$..*"` | + +The above table MUST be serialized as a tuple. In JSON, this SHOULD be represented as an array containing the values (but not keys) sequenced in the order they appear in the table. + +## 5.1.2 IPLD Schema + +``` ipldsch +type Promise struct { + promised Target + actionlabel String -- The label inside the invocation + jsonpath String (implicit "$..*") -- JSONPath Expression +} representation tuple + +type Target enum { + | Local (rename "/") + | Rmeote(CID) +} +``` + +## 5.1.3 JSON Examples + +``` js +// All outputs + +["/", "some-label"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "QmeURvKzGm85Ekt1t1wDJ3niHAXuhexi281N2ig311pLZv"] + +["/", "some-label", "$..*"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label", "$..*"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "QmeURvKzGm85Ekt1t1wDJ3niHAXuhexi281N2ig311pLZv", "$..*"] + +// Only the status code +["/", "some-label" "$payload.users[0].employer.name"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label", "$payload.users[0].employer.name"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label", "$payload.users[0].employer.name"] +["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "QmeURvKzGm85Ekt1t1wDJ3niHAXuhexi281N2ig311pLZv", "$payload.users[0].employer.name"] +``` + +## 5.2 Pipelining + +Pipelining uses promises as inputs to determine the required dataflow graph. The following examples both express the following dataflow graph: ``` ┌────────────────────────────┐ @@ -260,7 +418,13 @@ The following examples both express the following dataflow graph: └────────────────────────────┘ ``` -#### 3.3.2.1 Batched +To keep things simple, we expect the shape of the return data of all of these steps to include: + +``` json +{"http": {"body": "some text"}} +``` + +#### 5.2.1 Batched ``` json { @@ -284,7 +448,7 @@ The following examples both express the following dataflow graph: { "to": "bob@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "update-dns", "$http.body"]} + "body": {"ucan/promise": ["/", "update-dns"]} } ] }, @@ -295,7 +459,7 @@ The following examples both express the following dataflow graph: { "to": "carol@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "update-dns", "$http.body"]} + "body": {"ucan/promise": ["/", "update-dns"]} } ] }, @@ -309,10 +473,10 @@ The following examples both express the following dataflow graph: "event": "email-notification", }, { - "_": {"ucan/promise": ["/", "notify-bob", "$http.body"]} + "_": {"ucan/promise": ["/", "notify-bob"]} }, { - "_": {"ucan/promise": ["/", "notify-carol", "$http.body"]} + "_": {"ucan/promise": ["/", "notify-carol"]} } ] } @@ -321,7 +485,7 @@ The following examples both express the following dataflow graph: } ``` -### 3.3.3 Serial Pipeline +### 5.2.2 Serial Pipeline ``` ┌────────────────────────────┐ @@ -443,125 +607,6 @@ The following examples both express the following dataflow graph: } ``` -# 4 Receipt - -An invocation receipt is an attestation of the output of an invocation. A receipt MUST be signed by the executor (the `aud` of the associated UCAN). - -Note that this does not guarantee correctness of the result! The statement's veracity MUST be only understood as an attestation from the executor. - -## 4.1 Fields - -| Field | Type | Description | Required | Default | -|----------------|--------------|--------------------------------------------------------------------------------------------------|----------|---------| -| `ucan/receipt` | `CID` | CID of the Invocation that generated this response | Yes | | -| `rlt` | `{CID: Any}` | The results of each call, indexed by the CID of the `dag-cbor` encoded [Action](#32-ipld-schema) | Yes | | -| `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | -| `nnc` | `String` | A unique nonce, to distinguish each receipt | Yes | | -| `ext` | `Any` | Non-normative extended fields | No | `null` | -| `sig` | `Bytes` | Signature of the rest of the field canonicalized | Yes | | - -## 4.1 IPLD Schema - -``` ipldsch -type Receipt struct { - rec &Invocation - rlt {URI : {Ability : Any}} - v SemVer - nnc String - ext optional Any - sig Bytes -} -``` - -## 4.2 JSON Example - -``` json -{ - "ucan/receipt": "QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", - "v": "0.1.0", - "rlt": { - "QmYqbJBxCqqzDouPMbcrbNuB3WHWajSyq4RWin7ufs2ajf": [ - { - "from": "alice@example.com", - "text": "Hello world!" - }, - { - "from": "bob@example.com", - "text": "What's up?" - } - ], - "QmXrfqKNUpRiyyi8r3hpSZyZuG3S2MY9rTw1p8iEC9FNh5": { - "http": { - "status": 200, - "body": "hello world" - }, - "ms": 476 - } - }, - "nnc": "xyz", - "ext": { - "notes": "very receipt. such wow.", - "tags": ["dag-house", "fission"] - }, - "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt_VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" - } -``` - -# 5 Promise Pipelining - -> 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 - -At UCAN creation time, the UCAN MAY not yet have all of the information required to construct the next request in a sequence. Waiting for each request to complete before proceeding to the next action 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 earlier invocations into the input of a later one. This liberates the invoker from waiting for each step. - -The authority to execute later actions often cannot be fully attenuated in advance, since the executor controls the reported output of the prior step in a pipeline. When choosing to use pipelining, the invoker MUST delegate capabilities for any of the possible outputs. If tight control is required to limit authority, pipelining SHOULD NOT be used. - -## 5.1 Fields - -| Field | Type | Description | Required | Default | -|-----------------|--------------|---------------------------------------------------------------------------------------|----------|----------| -| `ucan/promised` | `CID or "/"` | The Invocation being referenced | Yes | | -| `label` | `String` | The action's label. If the actions were implicit (`"run": "*"`), the the CID is used | Yes | | -| `selector` | `String` | The [JSONPath](https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/) expression | No | `"$..*"` | - -The above table MUST be serialized as a tuple. In JSON, this SHOULD be represented as an array containing the values (but not keys) sequenced in the order they appear in the table. - -## 5.2 IPLD Schema - -``` ipldsch -type Promise struct { - promised Target - actionlabel String -- The label inside the invocation - jsonpath String (implicit "$..*") -- JSONPath Expression -} representation tuple - -type Target enum { - | Local (rename "/") - | Rmeote(CID) -} -``` - -## 5.3 JSON Examples - -``` js -// All outputs - -["/", "some-label"] -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label"] -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "QmeURvKzGm85Ekt1t1wDJ3niHAXuhexi281N2ig311pLZv"] - -["/", "some-label", "$..*"] -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label", "$..*"] -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "QmeURvKzGm85Ekt1t1wDJ3niHAXuhexi281N2ig311pLZv", "$..*"] - -// Only the status code -["/", "some-label" "$payload.users[0].employer.name"] -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label", "$payload.users[0].employer.name"] -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label", "$payload.users[0].employer.name"] -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "QmeURvKzGm85Ekt1t1wDJ3niHAXuhexi281N2ig311pLZv", "$payload.users[0].employer.name"] -``` - # 6 Appendix ## 6.1 Support Types @@ -606,6 +651,8 @@ Many thanks to [Mark Miller](https://github.com/erights) for his [pioneering wor 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 [Quinn Wilton](https://github.com/QuinnWilton) and [Marc-Antoine Parent](https://github.com/maparent) for their 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](https://github.com/QuinnWilton) for her discussion of speech acts, and for sharing her positives experiences with JSONPath. 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). From e87b317ffa09455c66cb4fbd89b8ce4996a923a0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 28 Nov 2022 00:36:46 -0800 Subject: [PATCH 065/154] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ae5138d..a99fa5a2 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,7 @@ type Action enum { "ucan/invoke": "QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "v": "0.1.0", "nnc": "abcdef", - "run": "*", // Exmplicitely "run all" + "run": "*", // Explicitly "run all" "ext": null, "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" } From 56891132dc5e49f280d0f9e26a954c4e1150e373 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 28 Nov 2022 00:40:19 -0800 Subject: [PATCH 066/154] Reintroduce selectors to example now that it's moved --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a99fa5a2..f4a497e9 100644 --- a/README.md +++ b/README.md @@ -448,7 +448,7 @@ To keep things simple, we expect the shape of the return data of all of these st { "to": "bob@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "update-dns"]} + "body": {"ucan/promise": ["/", "update-dns", "$http.body"]} } ] }, @@ -459,7 +459,7 @@ To keep things simple, we expect the shape of the return data of all of these st { "to": "carol@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "update-dns"]} + "body": {"ucan/promise": ["/", "update-dns", "$http.body"]} } ] }, @@ -595,10 +595,10 @@ To keep things simple, we expect the shape of the return data of all of these st "event": "email-notification", }, { - "_": {"ucan/promise": ["/", "notify-bob", "$http.body"]} + "_": {"ucan/promise": ["/", "notify-bob"]} }, { - "_": {"ucan/promise": ["QmaCRSugy2sXEKxjDDwcqLx2Bs7CUGpZSrtsASsNyntyC9", "notify-carol", "$http.body"]} + "_": {"ucan/promise": ["QmaCRSugy2sXEKxjDDwcqLx2Bs7CUGpZSrtsASsNyntyC9", "notify-carol"]} } ] } From a79dcc9e5edd1c6f4c6db0aab1577dc5e795e6c3 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 28 Nov 2022 00:52:25 -0800 Subject: [PATCH 067/154] Link to Mark Miller --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f4a497e9..36b9eee0 100644 --- a/README.md +++ b/README.md @@ -335,7 +335,7 @@ type Receipt struct { > 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](https://github.com/erights), [Robust Composition](http://www.erights.org/talks/thesis/markm-thesis.pdf) At UCAN creation time, the UCAN MAY not yet have all of the information required to construct the next request in a sequence. Waiting for each request to complete before proceeding to the next action 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 earlier invocations into the input of a later one. This liberates the invoker from waiting for each step. From 9e638d353cc1613bb942d40160ef21ad0512a85c Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 28 Nov 2022 01:03:01 -0800 Subject: [PATCH 068/154] Consistency --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 36b9eee0..26542d3e 100644 --- a/README.md +++ b/README.md @@ -335,7 +335,7 @@ type Receipt struct { > 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](https://github.com/erights), [Robust Composition](http://www.erights.org/talks/thesis/markm-thesis.pdf) At UCAN creation time, the UCAN MAY not yet have all of the information required to construct the next request in a sequence. Waiting for each request to complete before proceeding to the next action 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 earlier invocations into the input of a later one. This liberates the invoker from waiting for each step. From 4dfd5b5571b0b391551d6819750dcd0da121362e Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 28 Nov 2022 16:42:46 +0000 Subject: [PATCH 069/154] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Philipp Krüger Signed-off-by: Brooklyn Zelenka --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 26542d3e..8f89e4a5 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,7 @@ If a capability input has the key `"_"` and the value is a promise, the input MU ### 3.1.4 Nonce -The `nnc` field MUS contain a unique nonce. This field makes each invocation unique. +The `nnc` field MUST contain a unique nonce. This field makes each invocation unique. ### 3.1.5 Extended Fields From 2668abce8b098fd1619fb9c9f47ce0a81a31f828 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 28 Nov 2022 16:45:24 +0000 Subject: [PATCH 070/154] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Philipp Krüger Signed-off-by: Brooklyn Zelenka --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8f89e4a5..403e5484 100644 --- a/README.md +++ b/README.md @@ -364,7 +364,7 @@ type Promise struct { type Target enum { | Local (rename "/") - | Rmeote(CID) + | Remote(CID) } ``` From e7aeae21bdddf137c20f2acc9a726e268993defa Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 28 Nov 2022 09:26:53 -0800 Subject: [PATCH 071/154] Make table and IPLD the same --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 403e5484..f34ec0e7 100644 --- a/README.md +++ b/README.md @@ -345,11 +345,11 @@ The authority to execute later actions often cannot be fully attenuated in advan ## 5.1.1 Fields -| Field | Type | Description | Required | Default | -|-----------------|--------------|---------------------------------------------------------------------------------------|----------|----------| -| `ucan/promised` | `CID or "/"` | The Invocation being referenced | Yes | | -| `label` | `String` | The action's label. If the actions were implicit (`"run": "*"`), the the CID is used | Yes | | -| `selector` | `String` | The [JSONPath](https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/) expression | No | `"$..*"` | +| Field | Type | Description | Required | Default | +|---------------|--------------|---------------------------------------------------------------------------------------|----------|----------| +| `promised` | `CID or "/"` | The Invocation being referenced | Yes | | +| `actionlabel` | `String` | The action's label. If the actions were implicit (`"run": "*"`), the the CID is used | Yes | | +| `selector` | `String` | The [JSONPath](https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/) expression | No | `"$..*"` | The above table MUST be serialized as a tuple. In JSON, this SHOULD be represented as an array containing the values (but not keys) sequenced in the order they appear in the table. @@ -357,9 +357,9 @@ The above table MUST be serialized as a tuple. In JSON, this SHOULD be represent ``` ipldsch type Promise struct { - promised Target + promised Target actionlabel String -- The label inside the invocation - jsonpath String (implicit "$..*") -- JSONPath Expression + selector String (implicit "$..*") -- JSONPath Expression } representation tuple type Target enum { From 2dd8912fd4da57efa1b5cfa2772f763f95da13af Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 28 Nov 2022 19:12:48 -0800 Subject: [PATCH 072/154] Fix typo (thanks Quinn!) --- README.md | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/README.md b/README.md index f34ec0e7..64ed3d68 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ The `v` field MUST contain the version of the invocation object schema. ### 3.1.3 Run Capabilities -The OPTIONAL `run` field MUST reference the actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](#5-promise-pipelining)) are intended to be run, then they MUST be prefixed with an arbitrary label and treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or lesser scope. +The OPTIONAL `run` field MUST reference the actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](#5-promise-pipelining)) are intended to be run, then they MUST be prefixed with an arbitrary label and treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or greater authority. #### 3.1.3.1 Promises @@ -349,7 +349,6 @@ The authority to execute later actions often cannot be fully attenuated in advan |---------------|--------------|---------------------------------------------------------------------------------------|----------|----------| | `promised` | `CID or "/"` | The Invocation being referenced | Yes | | | `actionlabel` | `String` | The action's label. If the actions were implicit (`"run": "*"`), the the CID is used | Yes | | -| `selector` | `String` | The [JSONPath](https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/) expression | No | `"$..*"` | The above table MUST be serialized as a tuple. In JSON, this SHOULD be represented as an array containing the values (but not keys) sequenced in the order they appear in the table. @@ -359,7 +358,6 @@ The above table MUST be serialized as a tuple. In JSON, this SHOULD be represent type Promise struct { promised Target actionlabel String -- The label inside the invocation - selector String (implicit "$..*") -- JSONPath Expression } representation tuple type Target enum { @@ -371,21 +369,9 @@ type Target enum { ## 5.1.3 JSON Examples ``` js -// All outputs - ["/", "some-label"] ["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label"] ["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "QmeURvKzGm85Ekt1t1wDJ3niHAXuhexi281N2ig311pLZv"] - -["/", "some-label", "$..*"] -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label", "$..*"] -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "QmeURvKzGm85Ekt1t1wDJ3niHAXuhexi281N2ig311pLZv", "$..*"] - -// Only the status code -["/", "some-label" "$payload.users[0].employer.name"] -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label", "$payload.users[0].employer.name"] -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label", "$payload.users[0].employer.name"] -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "QmeURvKzGm85Ekt1t1wDJ3niHAXuhexi281N2ig311pLZv", "$payload.users[0].employer.name"] ``` ## 5.2 Pipelining From 0dac02da60fc9c30bb3919553de5a8fa8c619edb Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 28 Nov 2022 19:14:30 -0800 Subject: [PATCH 073/154] rename inv to ucan/invoke (thanks Philipp!) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64ed3d68..67c4b812 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,7 @@ If serialized as JSON, the `sig` field MUST be serialized as [unpadded base64url ``` ipldsch type Invocation struct { - inv &UCAN -- The UCAN providing authority + inv &UCAN (rename "ucan/invoke") -- The UCAN providing authority v SemVer -- Version run Scope -- Which actions to invoke nnc String -- Nonce From 3e336f8a669e535b16fe882d9fcb14d3211647e9 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 06:00:14 +0000 Subject: [PATCH 074/154] Update README.md Co-authored-by: Irakli Gozalishvili Signed-off-by: Brooklyn Zelenka --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 67c4b812..ad442905 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ The `nnc` field MUST contain a unique nonce. This field makes each invocation un ### 3.1.5 Extended Fields -The OPTIONAL `ext` field MAY contain arbitrary data. If not present, the `ext` field MUST me interpreted as `null`, including for [signature](#315-signature). +The OPTIONAL `ext` field MAY contain arbitrary data. If not present, the `ext` field MUST be interpreted as `null`, including for [signature](#315-signature). ### 3.1.6 Signature From a5c2cb68438d2b0830d182453dacd91545a02aa7 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 06:03:28 +0000 Subject: [PATCH 075/154] Update README.md Co-authored-by: Irakli Gozalishvili Signed-off-by: Brooklyn Zelenka --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ad442905..0868932e 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,7 @@ type Invocation struct { v SemVer -- Version run Scope -- Which actions to invoke nnc String -- Nonce - ext Any -- Extended fields + ext nullable Any (implicit null) -- Extended fields sig Bytes -- Signature } From ed328f28620eee93227adc1254ee848304a6683e Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 06:14:34 +0000 Subject: [PATCH 076/154] Update README.md Co-authored-by: Irakli Gozalishvili Signed-off-by: Brooklyn Zelenka --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0868932e..29a01cc2 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ type Invocation struct { inv &UCAN (rename "ucan/invoke") -- The UCAN providing authority v SemVer -- Version run Scope -- Which actions to invoke - nnc String -- Nonce + nnc String (implicit "") -- Nonce ext nullable Any (implicit null) -- Extended fields sig Bytes -- Signature } From 19bf685946dfcce67f6c18b3ae2635817725e55c Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 28 Nov 2022 22:13:38 -0800 Subject: [PATCH 077/154] Get rid of (confusing) retries field --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 29a01cc2..0e7f7209 100644 --- a/README.md +++ b/README.md @@ -424,7 +424,7 @@ To keep things simple, we expect the shape of the return data of all of these st "do": "crud/update", "inputs": { "value": "hello world", - "retries": 5 + "content-type": "text/plain; charset=utf-8" } }, "notify-bob": { @@ -525,7 +525,7 @@ To keep things simple, we expect the shape of the return data of all of these st "inputs": [ { "value": "hello world", - "retries": 5 + "content-type": "text/plain; charset=utf-8" } ] } From f6fc33829d3d2f8547f773f358c8abba97f39c36 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 06:15:23 +0000 Subject: [PATCH 078/154] Update README.md Co-authored-by: Irakli Gozalishvili Signed-off-by: Brooklyn Zelenka --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0e7f7209..43bbf3c4 100644 --- a/README.md +++ b/README.md @@ -190,9 +190,11 @@ type Invocation struct { run Scope -- Which actions to invoke nnc String (implicit "") -- Nonce ext nullable Any (implicit null) -- Extended fields - sig Bytes -- Signature + sig VarSig -- Signature } +type VarSig Bytes + type Scope enum { | All ("*") | Action From d886fce7c2b5ce8c5cf9d6a1c5e68d4037ab8e2a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 28 Nov 2022 22:40:45 -0800 Subject: [PATCH 079/154] Take multiple CIDs --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 43bbf3c4..8d33f185 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ The invocation wrapper MUST be signed by the same principal that issued the UCAN | Field | Type | Value | Description | Required | Default | |---------------|----------------------------------------------|-----------|--------------------------------------------------|----------|---------| -| `ucan/invoke` | `CID` | | The CID of the UCAN to invoke | Yes | | +| `ucan/invoke` | `[&UCAN]` | | The CID of UCANs to invoke | Yes | | | `v` | `SemVer` | `"0.1.0"` | SemVer of the UCAN invocation this schema | Yes | | | `run` | `"*" or {String: {URI : {Ability : [Any]}}}` | | Which UCAN capabilities to run | No | `"*"` | | `nnc` | `String` | | A unique nonce, to distinguish each invocation | Yes | | @@ -169,7 +169,7 @@ If a capability input has the key `"_"` and the value is a promise, the input MU ### 3.1.4 Nonce -The `nnc` field MUST contain a unique nonce. This field makes each invocation unique. +The `nnc` field MUST contain a unique nonce. This field exists to enable making the CID of each invocation unique. While this field MAY be an empty string, it is NOT RECOMMENDED. ### 3.1.5 Extended Fields @@ -185,11 +185,11 @@ If serialized as JSON, the `sig` field MUST be serialized as [unpadded base64url ``` ipldsch type Invocation struct { - inv &UCAN (rename "ucan/invoke") -- The UCAN providing authority + inv [&UCAN] (rename "ucan/invoke") -- The UCAN providing authority v SemVer -- Version run Scope -- Which actions to invoke - nnc String (implicit "") -- Nonce - ext nullable Any (implicit null) -- Extended fields + nnc String -- Nonce + ext nullable Any (implicit null) -- Extended fields sig VarSig -- Signature } From e19c70bceaa1570aefd37d2c97502fd77e944958 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 28 Nov 2022 23:06:21 -0800 Subject: [PATCH 080/154] No more Qm --- README.md | 52 ++++++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 8d33f185..b1990b31 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,7 @@ type Action enum { ``` js { - "ucan/invoke": "QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", + "ucan/invoke": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], "v": "0.1.0", "nnc": "abcdef", "ext": null, @@ -223,7 +223,7 @@ type Action enum { ``` js { - "ucan/invoke": "QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", + "ucan/invoke": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], "v": "0.1.0", "nnc": "abcdef", "run": "*", // Explicitly "run all" @@ -238,7 +238,7 @@ Promise pipelines are handled in more detail in [section 5](#5-promise-pipelinin ``` js { - "ucan/invoke": "QmY4jEVE35u8SHegWoDak2x6vTAZ6Cc4cpSD5LAUQ23W7L", + "ucan/invoke": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], "v": "0.1.0", "nnc": "02468", "ext": null, @@ -303,10 +303,10 @@ type Receipt struct { ``` json { - "ucan/receipt": "QmWqWBitVJX69QrEjzKsVTy3KQRK6snUoHaPSjmQpxvP1f", + "ucan/receipt": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], "v": "0.1.0", "rlt": { - "QmYqbJBxCqqzDouPMbcrbNuB3WHWajSyq4RWin7ufs2ajf": [ + "bafkreiakkqwuffzsxrzseo7fweeicqlnqanhvyffjieh3etsbnnkjxbphi": [ { "from": "alice@example.com", "text": "Hello world!" @@ -316,7 +316,7 @@ type Receipt struct { "text": "What's up?" } ], - "QmXrfqKNUpRiyyi8r3hpSZyZuG3S2MY9rTw1p8iEC9FNh5": { + "bafkreienxymjwglxlb3rkdyeyjt54ddoe4x4qi7a7hyfce3z56zspmy6pm": { "http": { "status": 200, "body": "hello world" @@ -359,12 +359,12 @@ The above table MUST be serialized as a tuple. In JSON, this SHOULD be represent ``` ipldsch type Promise struct { promised Target - actionlabel String -- The label inside the invocation + actionlabel String -- The label inside the invocation } representation tuple -type Target enum { - | Local (rename "/") - | Remote(CID) +type Target union { + | Local "/" + | &Invocation } ``` @@ -372,8 +372,8 @@ type Target enum { ``` js ["/", "some-label"] -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "some-label"] -["QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", "QmeURvKzGm85Ekt1t1wDJ3niHAXuhexi281N2ig311pLZv"] +["bafkreiddwsbb7fasjb6k6jodakprtl4lhw6h3g4k7tew7vwwvd63veviie", "some-label"] +["bafkreiddwsbb7fasjb6k6jodakprtl4lhw6h3g4k7tew7vwwvd63veviie", "bafkreidlqsd6nh6hdgwhr4machsvusobpvn4qfrxfgl5vowoggzk2xpldq"] ``` ## 5.2 Pipelining @@ -406,17 +406,11 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The └────────────────────────────┘ ``` -To keep things simple, we expect the shape of the return data of all of these steps to include: - -``` json -{"http": {"body": "some text"}} -``` - #### 5.2.1 Batched ``` json { - "ucan/invoke": "QmYW8Z58V1v8R25USVPUuFHtU7nGouApdGTk3vRPXmVHPR", + "ucan/invoke": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], "v": "0.1.0", "nnc": "abcdef", "ext": null, @@ -436,7 +430,7 @@ To keep things simple, we expect the shape of the return data of all of these st { "to": "bob@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "update-dns", "$http.body"]} + "body": {"ucan/promise": ["/", "update-dns"]} } ] }, @@ -447,7 +441,7 @@ To keep things simple, we expect the shape of the return data of all of these st { "to": "carol@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "update-dns", "$http.body"]} + "body": {"ucan/promise": ["/", "update-dns"]} } ] }, @@ -516,7 +510,7 @@ To keep things simple, we expect the shape of the return data of all of these st ``` json { - "ucan/invoke": "Qmd4trNUhgWwsBbSsYBEWJqgiHyLrnhZ8u1DJsWsEKeuuF", + "ucan/invoke": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], "v": "0.1.0", "nnc": "abcdef", "ext": null, @@ -536,7 +530,7 @@ To keep things simple, we expect the shape of the return data of all of these st } { - "ucan/invoke": "QmbXdT8QQMJ55Lb6MGJqTmwNzuUnHsE18t7zXGWeq9rQcV", + "ucan/invoke": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], "v": "0.1.0", "nnc": "12345", "ext": null, @@ -548,7 +542,7 @@ To keep things simple, we expect the shape of the return data of all of these st { "to": "carol@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["QmcKi4Z1L7VH4HahyrXb8Rzjex5U7DiY12PnTnysxviuDT", "update-dns", "$http.body"]} + "body": {"ucan/promise": ["bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu", "update-dns"]} } ] } @@ -557,7 +551,7 @@ To keep things simple, we expect the shape of the return data of all of these st } { - "ucan/invoke": "QmY4jEVE35u8SHegWoDak2x6vTAZ6Cc4cpSD5LAUQ23W7L", + "ucan/invoke": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], "v": "0.1.0", "nnc": "02468", "ext": null, @@ -569,7 +563,7 @@ To keep things simple, we expect the shape of the return data of all of these st { "to": "bob@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["QmcKi4Z1L7VH4HahyrXb8Rzjex5U7DiY12PnTnysxviuDT", "update-dns", "$http.body"]} + "body": {"ucan/promise": ["bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu", "update-dns"]} } ] }, @@ -586,7 +580,7 @@ To keep things simple, we expect the shape of the return data of all of these st "_": {"ucan/promise": ["/", "notify-bob"]} }, { - "_": {"ucan/promise": ["QmaCRSugy2sXEKxjDDwcqLx2Bs7CUGpZSrtsASsNyntyC9", "notify-carol"]} + "_": {"ucan/promise": ["bafkreidcqdxosqave5u5pml3pyikiglozyscgqikvb6foppobtk3hwkjn4", "notify-carol"]} } ] } @@ -641,6 +635,8 @@ Thanks to [Christine Lemmer-Webber](https://github.com/cwebber) for the many con 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, and for sharing her positives experiences with JSONPath. +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](https://github.com/blaine) for sharing their experiences with OAuth 1, irreversable design decisions, and advocating for keeping the spec simple-but-evolvable. 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). From e9a914ec1a35c3f0d6e2d9348587744f671499a0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 28 Nov 2022 23:17:58 -0800 Subject: [PATCH 081/154] Update dictionary --- .github/workflows/words-to-ignore.txt | 2 ++ README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 566a2635..4faddb0e 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -19,6 +19,7 @@ JWT JWT-encoded Lemmer-Webber Marsen +OAuth OCapN Pre-Draft Proto @@ -54,6 +55,7 @@ referentially requestor's responder signalling +simple-but-evolvable trustless ucanto unpadded diff --git a/README.md b/README.md index b1990b31..72e4d4e6 100644 --- a/README.md +++ b/README.md @@ -637,6 +637,6 @@ Thanks to [Marc-Antoine Parent](https://github.com/maparent) for his discussions 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](https://github.com/blaine) for sharing their experiences with OAuth 1, irreversable 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. 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). From 496570b47109521ebcd70a0a849f5d6d23dc8720 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 00:00:51 -0800 Subject: [PATCH 082/154] Update layout per Philipp, multiple UCANs in a prf --- README.md | 415 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 216 insertions(+), 199 deletions(-) diff --git a/README.md b/README.md index 72e4d4e6..7245dcb7 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ ## Depends On -* [UCAN](https://github.com/ucan-wg/spec/) +* [`dag-cbor`](https://ipld.io/specs/codecs/dag-cbor/spec/) +* [`ucan`](https://github.com/ucan-wg/spec/) * [`ucan-ipld`](https://github.com/ucan-wg/ucan-ipld/) # 0 Abstract @@ -140,18 +141,18 @@ The invocation wrapper MUST be signed by the same principal that issued the UCAN ## 3.1 Fields -| Field | Type | Value | Description | Required | Default | -|---------------|----------------------------------------------|-----------|--------------------------------------------------|----------|---------| -| `ucan/invoke` | `[&UCAN]` | | The CID of UCANs to invoke | Yes | | -| `v` | `SemVer` | `"0.1.0"` | SemVer of the UCAN invocation this schema | Yes | | -| `run` | `"*" or {String: {URI : {Ability : [Any]}}}` | | Which UCAN capabilities to run | No | `"*"` | -| `nnc` | `String` | | A unique nonce, to distinguish each invocation | Yes | | -| `ext` | `Any` | | Non-normative extended fields | No | `null` | -| `sig` | `Bytes` | | Signature of the rest of the field canonicalized | Yes | | +| Field | Type | Value | Description | Required | Default | +|-------|----------------------------------------------|-----------|---------------------------------------------------------|----------|---------| +| `v` | `SemVer` | `"0.1.0"` | SemVer of the UCAN invocation this schema | Yes | | +| `prf` | `[&UCAN]` | | The CIDs of the UCANs that provide the authority to run | Yes | | +| `run` | `"*" or {String: {URI : {Ability : [Any]}}}` | | Which UCAN capabilities to run | Yes | | +| `nnc` | `String` | | A unique nonce, to distinguish each invocation | Yes | | +| `ext` | `Any` | | Non-normative extended fields | No | `null` | +| `sig` | `Bytes` | | Signature of the rest of the field canonicalized | Yes | | -### 3.1.1 Invocation +### 3.1.1 Proofs -The `ucan/invoke` field MUST contain CIDs pointing to the UCANs to invoke. The outmost UCAN being invoked SHOULD NOT contain any actions that are not intended to be executed. +The `prf` field MUST contain CIDs pointing to the UCANs that provide the authority to run these actions. Restricting the outmost UCANs to only the authprity required for the current invocation is RECOMMENDED. ### 3.1.2 Version @@ -185,11 +186,15 @@ If serialized as JSON, the `sig` field MUST be serialized as [unpadded base64url ``` ipldsch type Invocation struct { - inv [&UCAN] (rename "ucan/invoke") -- The UCAN providing authority - v SemVer -- Version - run Scope -- Which actions to invoke - nnc String -- Nonce - ext nullable Any (implicit null) -- Extended fields + prf [&UCAN] -- The UCANs providing authority + v SemVer -- Version + run Scope -- Which actions to invoke + nnc String -- Nonce + ext nullable Any (implicit null) -- Extended fields +} + +type SignedInvocation struct { + inv Invocation (rename "ucan/invoke") sig VarSig -- Signature } @@ -213,21 +218,13 @@ type Action enum { ``` js { - "ucan/invoke": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], - "v": "0.1.0", - "nnc": "abcdef", - "ext": null, - "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" -} -``` - - ``` js -{ - "ucan/invoke": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], - "v": "0.1.0", - "nnc": "abcdef", - "run": "*", // Explicitly "run all" - "ext": null, + "ucan/invoke": { + "v": "0.1.0", + "prf": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], + "nnc": "abcdef", + "run": "*", // Explicitly "run all" + "ext": null + } "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" } ``` @@ -238,30 +235,32 @@ Promise pipelines are handled in more detail in [section 5](#5-promise-pipelinin ``` js { - "ucan/invoke": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], - "v": "0.1.0", - "nnc": "02468", - "ext": null, - "run": { - "notify-bob": { - "using": "mailto://alice@example.com", - "do": "msg/send", - "inputs": [ - { - "to": "bob@example.com", - "subject": "DNSLink for example.com", - "body": "Hello Bob!" + "ucan/invoke": { + "v": "0.1.0", + "prf": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], + "nnc": "02468", + "ext": null, + "run": { + "notify-bob": { + "using": "mailto://alice@example.com", + "do": "msg/send", + "inputs": [ + { + "to": "bob@example.com", + "subject": "DNSLink for example.com", + "body": "Hello Bob!" + } + ] + }, + "log-as-done": { + "using": "https://example.com/report" + "do": "crud/update" + "inputs": { + "from": "mailto://alice@exmaple.com", + "to": ["bob@exmaple.com"], + "event": "email-notification", + "value": {"ucan/promise": ["/", "notify-bob"]} // Pipelined promise } - ] - }, - "log-as-done": { - "using": "https://example.com/report" - "do": "crud/update" - "inputs": { - "from": "mailto://alice@exmaple.com", - "to": ["bob@exmaple.com"], - "event": "email-notification", - "value": {"ucan/promise": ["/", "notify-bob"]} // Pipelined promise } } }, @@ -277,58 +276,66 @@ Note that this does not guarantee correctness of the result! The statement's ver ## 4.1 Fields -| Field | Type | Description | Required | Default | -|----------------|--------------|--------------------------------------------------------------------------------------------------|----------|---------| -| `ucan/receipt` | `CID` | CID of the Invocation that generated this response | Yes | | -| `rlt` | `{CID: Any}` | The results of each call, indexed by the CID of the `dag-cbor` encoded [Action](#32-ipld-schema) | Yes | | -| `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | -| `nnc` | `String` | A unique nonce, to distinguish each receipt | Yes | | -| `ext` | `Any` | Non-normative extended fields | No | `null` | -| `sig` | `Bytes` | Signature of the rest of the field canonicalized | Yes | | +| Field | Type | Description | Required | Default | +|-------|---------------|--------------------------------------------------------------------------------------------------|----------|---------| +| `inv` | `&Invocation` | CID of the Invocation that generated this response | Yes | | +| `rlt` | `{CID: Any}` | The results of each call, indexed by the CID of the `dag-cbor` encoded [Action](#32-ipld-schema) | Yes | | +| `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | +| `nnc` | `String` | A unique nonce, to distinguish each receipt | Yes | | +| `ext` | `Any` | Non-normative extended fields | No | `null` | +| `sig` | `Bytes` | Signature of the rest of the field canonicalized | Yes | | ## 4.1 IPLD Schema ``` ipldsch type Receipt struct { - rec &Invocation + inv &SignedInvocation rlt {URI : {Ability : Any}} v SemVer nnc String ext optional Any - sig Bytes } + +type SignedReceipt struct { + rec Receipt (rename "ucan/receipt") + sig VarSig +} + +type VarSig Bytes ``` ## 4.2 JSON Example ``` json { - "ucan/receipt": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], - "v": "0.1.0", - "rlt": { - "bafkreiakkqwuffzsxrzseo7fweeicqlnqanhvyffjieh3etsbnnkjxbphi": [ - { - "from": "alice@example.com", - "text": "Hello world!" - }, - { - "from": "bob@example.com", - "text": "What's up?" + "ucan/receipt": { + "v": "0.1.0", + "inv": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e", + "rlt": { + "bafkreiakkqwuffzsxrzseo7fweeicqlnqanhvyffjieh3etsbnnkjxbphi": [ + { + "from": "alice@example.com", + "text": "Hello world!" + }, + { + "from": "bob@example.com", + "text": "What's up?" + } + ], + "bafkreienxymjwglxlb3rkdyeyjt54ddoe4x4qi7a7hyfce3z56zspmy6pm": { + "http": { + "status": 200, + "body": "hello world" + }, + "ms": 476 } - ], - "bafkreienxymjwglxlb3rkdyeyjt54ddoe4x4qi7a7hyfce3z56zspmy6pm": { - "http": { - "status": 200, - "body": "hello world" - }, - "ms": 476 + }, + "nnc": "xyz", + "ext": { + "notes": "very receipt. such wow.", + "tags": ["dag-house", "fission"] } }, - "nnc": "xyz", - "ext": { - "notes": "very receipt. such wow.", - "tags": ["dag-house", "fission"] - }, "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt_VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" } ``` @@ -410,57 +417,59 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The ``` json { - "ucan/invoke": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], - "v": "0.1.0", - "nnc": "abcdef", - "ext": null, - "run": { - "update-dns" : { - "using": "dns://example.com?TYPE=TXT": - "do": "crud/update", - "inputs": { - "value": "hello world", - "content-type": "text/plain; charset=utf-8" - } - }, - "notify-bob": { - "using": "mailto://alice@example.com", - "do": "msg/send", - "inputs": [ - { - "to": "bob@example.com", - "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "update-dns"]} - } - ] - }, - "notify-carol": { - "using": "mailto://alice@example.com", - "do": "msg/send", - "inputs": [ - { - "to": "carol@example.com", - "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "update-dns"]} - } - ] - }, - "log-as-done": { - "using": "https://example.com/report" - "do": "crud/update" - "inputs": [ - { - "from": "mailto://alice@exmaple.com", - "to": ["bob@exmaple.com", "carol@example.com"], - "event": "email-notification", - }, - { - "_": {"ucan/promise": ["/", "notify-bob"]} - }, - { - "_": {"ucan/promise": ["/", "notify-carol"]} + "ucan/invoke": { + "v": "0.1.0", + "prf": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], + "nnc": "abcdef", + "ext": null, + "run": { + "update-dns" : { + "using": "dns://example.com?TYPE=TXT": + "do": "crud/update", + "inputs": { + "value": "hello world", + "content-type": "text/plain; charset=utf-8" } - ] + }, + "notify-bob": { + "using": "mailto://alice@example.com", + "do": "msg/send", + "inputs": [ + { + "to": "bob@example.com", + "subject": "DNSLink for example.com", + "body": {"ucan/promise": ["/", "update-dns"]} + } + ] + }, + "notify-carol": { + "using": "mailto://alice@example.com", + "do": "msg/send", + "inputs": [ + { + "to": "carol@example.com", + "subject": "DNSLink for example.com", + "body": {"ucan/promise": ["/", "update-dns"]} + } + ] + }, + "log-as-done": { + "using": "https://example.com/report" + "do": "crud/update" + "inputs": [ + { + "from": "mailto://alice@exmaple.com", + "to": ["bob@exmaple.com", "carol@example.com"], + "event": "email-notification", + }, + { + "_": {"ucan/promise": ["/", "notify-bob"]} + }, + { + "_": {"ucan/promise": ["/", "notify-carol"]} + } + ] + } } }, "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" @@ -510,79 +519,85 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The ``` json { - "ucan/invoke": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], - "v": "0.1.0", - "nnc": "abcdef", - "ext": null, - "run": { - "update-dns": { - "using": "dns://example.com?TYPE=TXT", - "do": "crud/update" - "inputs": [ - { - "value": "hello world", - "content-type": "text/plain; charset=utf-8" - } - ] + "ucan/invoke": { + "v": "0.1.0", + "inv": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], + "nnc": "abcdef", + "ext": null, + "run": { + "update-dns": { + "using": "dns://example.com?TYPE=TXT", + "do": "crud/update" + "inputs": [ + { + "value": "hello world", + "content-type": "text/plain; charset=utf-8" + } + ] + } } }, "sig": "kQHtTruysx4S8SrvSjTwr6ttTLzc7dd7atANUYT-SRK6v_SX8bjHegWoDak2x6vTAZ6CcVKBt6JGpgnjABpsoL" } { - "ucan/invoke": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], - "v": "0.1.0", - "nnc": "12345", - "ext": null, - "run": { - "notify-carol": { - "using": "mailto://alice@example.com", - "do": "msg/send", - "inputs": [ - { - "to": "carol@example.com", - "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu", "update-dns"]} - } - ] + "ucan/invoke": { + "v": "0.1.0", + "prf": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], + "nnc": "12345", + "ext": null, + "run": { + "notify-carol": { + "using": "mailto://alice@example.com", + "do": "msg/send", + "inputs": [ + { + "to": "carol@example.com", + "subject": "DNSLink for example.com", + "body": {"ucan/promise": ["bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu", "update-dns"]} + } + ] + } } }, "sig": "XZRSmp5cHaXX6xWzSTxQqC95kQHtTruysx4S8SrvSjTwr6ttTLzc7dd7atANUQJXoWThUiVuCHWdMnQNQJgiJi" } { - "ucan/invoke": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], - "v": "0.1.0", - "nnc": "02468", - "ext": null, - "run": { - "notify-bob": { - "using": "mailto://alice@example.com", - "do": "msg/send", - "inputs": [ - { - "to": "bob@example.com", - "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu", "update-dns"]} - } - ] - }, - "log-as-done": { - "using": "https://example.com/report" - "do": "crud/update" - "inputs": [ - { - "from": "mailto://alice@exmaple.com", - "to": ["bob@exmaple.com", "carol@example.com"], - "event": "email-notification", - }, - { - "_": {"ucan/promise": ["/", "notify-bob"]} - }, - { - "_": {"ucan/promise": ["bafkreidcqdxosqave5u5pml3pyikiglozyscgqikvb6foppobtk3hwkjn4", "notify-carol"]} - } - ] + "ucan/invoke": { + "v": "0.1.0", + "prf": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], + "nnc": "02468", + "ext": null, + "run": { + "notify-bob": { + "using": "mailto://alice@example.com", + "do": "msg/send", + "inputs": [ + { + "to": "bob@example.com", + "subject": "DNSLink for example.com", + "body": {"ucan/promise": ["bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu", "update-dns"]} + } + ] + }, + "log-as-done": { + "using": "https://example.com/report" + "do": "crud/update" + "inputs": [ + { + "from": "mailto://alice@exmaple.com", + "to": ["bob@exmaple.com", "carol@example.com"], + "event": "email-notification", + }, + { + "_": {"ucan/promise": ["/", "notify-bob"]} + }, + { + "_": {"ucan/promise": ["bafkreidcqdxosqave5u5pml3pyikiglozyscgqikvb6foppobtk3hwkjn4", "notify-carol"]} + } + ] + } } }, "sig": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7" @@ -631,6 +646,8 @@ The [Object Capability Network (OCapN)](https://github.com/ocapn/ocapn) protocol 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 [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 [Christine Lemmer-Webber](https://github.com/cwebber) for the many conversations about capability systems and the programming models that they enable. 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. @@ -639,4 +656,4 @@ Many thanks to [Quinn Wilton](https://github.com/QuinnWilton) for her discussion 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. -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 [Philipp Krüger](https://github.com/matheus23/) for the enthusiastic feedback on the overall design and encoding. From dd3815de8cfceafc5bfe5bab5fb620462d30fa64 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 00:03:03 -0800 Subject: [PATCH 083/154] Update dictionary --- .github/workflows/words-to-ignore.txt | 2 ++ README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 4faddb0e..67729f4e 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -17,10 +17,12 @@ JSON JSONPath JWT JWT-encoded +Krüger Lemmer-Webber Marsen OAuth OCapN +Philipp Pre-Draft Proto RPC diff --git a/README.md b/README.md index 7245dcb7..f78e28b5 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ The invocation wrapper MUST be signed by the same principal that issued the UCAN ### 3.1.1 Proofs -The `prf` field MUST contain CIDs pointing to the UCANs that provide the authority to run these actions. Restricting the outmost UCANs to only the authprity required for the current invocation is RECOMMENDED. +The `prf` field MUST contain CIDs pointing to the UCANs that provide the authority to run these actions. Restricting the outmost UCANs to only the authority required for the current invocation is RECOMMENDED. ### 3.1.2 Version From 47c5719d3b7232516e8b05ddec2e4c74057435c5 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 00:08:23 -0800 Subject: [PATCH 084/154] Typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f78e28b5..cb5e0389 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,7 @@ The `v` field MUST contain the version of the invocation object schema. ### 3.1.3 Run Capabilities -The OPTIONAL `run` field MUST reference the actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](#5-promise-pipelining)) are intended to be run, then they MUST be prefixed with an arbitrary label and treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or greater authority. +The `run` field MUST reference the actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](#5-promise-pipelining)) are intended to be run, then they MUST be prefixed with an arbitrary label and treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or greater authority. #### 3.1.3.1 Promises @@ -224,7 +224,7 @@ type Action enum { "nnc": "abcdef", "run": "*", // Explicitly "run all" "ext": null - } + }, "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" } ``` From 921569f0edcc25b42631884cdd1bf5f7eb413ec4 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 10:38:36 -0800 Subject: [PATCH 085/154] Use DAG-JSON consistly --- README.md | 70 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index cb5e0389..cbe79e5c 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ ## Depends On -* [`dag-cbor`](https://ipld.io/specs/codecs/dag-cbor/spec/) -* [`ucan`](https://github.com/ucan-wg/spec/) -* [`ucan-ipld`](https://github.com/ucan-wg/ucan-ipld/) +* [DAG-CBOR](https://ipld.io/specs/codecs/dag-cbor/spec/) +* [UCAN](https://github.com/ucan-wg/spec/) +* [UCAN-IPLD](https://github.com/ucan-wg/ucan-ipld/) # 0 Abstract @@ -110,6 +110,20 @@ As we shall see in the [discussion of promise pipelining](#5-promise-pipelining) └─────────────────────────────────────────────────────────────────────────────────────────────────────────┘ ``` +## 1.3 A Note On Serialization + +The JSON examples below are given in [DAG-JSON](https://ipld.io/docs/codecs/known/dag-json/), but UCAN Invocation is actually defined as IPLD. This makes UCAN Invocation agnotsic to encoding. DAG-JSON follows particular conventions around wrapping CIDs and binary data in tags like so: + +``` json +// CID +{"/": Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD"} + +// Bytes (e.g. signature) +{"/": {"bytes": "s0m3Byte5"}} +``` + +This format help disambiguate type information in generical 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. + # 2 Roles Invocation adds two new roles to UCAN: invoker and executor. The existing UCAN delegator and delegate principals MUST persist to the invocation. @@ -152,7 +166,7 @@ The invocation wrapper MUST be signed by the same principal that issued the UCAN ### 3.1.1 Proofs -The `prf` field MUST contain CIDs pointing to the UCANs that provide the authority to run these actions. Restricting the outmost UCANs to only the authority required for the current invocation is RECOMMENDED. +The `prf` field MUST contain CIDs pointing to the UCANs that provide the authority to run these actions. The elements of this array MUST be sorted in ascending numeric order. Restricting the outmost UCANs to only the authority required for the current invocation is RECOMMENDED. ### 3.1.2 Version @@ -178,7 +192,7 @@ The OPTIONAL `ext` field MAY contain arbitrary data. If not present, the `ext` f ### 3.1.6 Signature -The `sig` field MUST contain the signature of the other fields. To construct the payload, the other fields MUST first be canonicalized as `dag-cbor`. +The `sig` field MUST contain the signature of the other fields. The signature MUST be given as a [VarSig](https://github.com/ChainAgnostic/varsig), which includes the payload encoding information. If serialized as JSON, the `sig` field MUST be serialized as [unpadded base64url](https://datatracker.ietf.org/doc/html/rfc4648#section-5). @@ -220,12 +234,12 @@ type Action enum { { "ucan/invoke": { "v": "0.1.0", - "prf": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], + "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "nnc": "abcdef", "run": "*", // Explicitly "run all" "ext": null }, - "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" + "sig": { "/": { "bytes:": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" } } } ``` @@ -237,7 +251,7 @@ Promise pipelines are handled in more detail in [section 5](#5-promise-pipelinin { "ucan/invoke": { "v": "0.1.0", - "prf": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], + "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "nnc": "02468", "ext": null, "run": { @@ -264,7 +278,7 @@ Promise pipelines are handled in more detail in [section 5](#5-promise-pipelinin } } }, - "sig": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7" + "sig": {"/": {"bytes:": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7"}} } ``` @@ -272,7 +286,7 @@ Promise pipelines are handled in more detail in [section 5](#5-promise-pipelinin An invocation receipt is an attestation of the output of an invocation. A receipt MUST be signed by the executor (the `aud` of the associated UCAN). -Note that this does not guarantee correctness of the result! The statement's veracity MUST be only understood as an attestation from the executor. +**NB: a Receipt this does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. ## 4.1 Fields @@ -290,7 +304,7 @@ Note that this does not guarantee correctness of the result! The statement's ver ``` ipldsch type Receipt struct { inv &SignedInvocation - rlt {URI : {Ability : Any}} + rlt {Hash : Any}} FIXME!!! v SemVer nnc String ext optional Any @@ -301,7 +315,7 @@ type SignedReceipt struct { sig VarSig } -type VarSig Bytes +type Hash Bytes ``` ## 4.2 JSON Example @@ -310,7 +324,7 @@ type VarSig Bytes { "ucan/receipt": { "v": "0.1.0", - "inv": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e", + "inv": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "rlt": { "bafkreiakkqwuffzsxrzseo7fweeicqlnqanhvyffjieh3etsbnnkjxbphi": [ { @@ -336,7 +350,7 @@ type VarSig Bytes "tags": ["dag-house", "fission"] } }, - "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt_VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" + "sig": {"/": {"bytes": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt_VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA"}} } ``` @@ -379,8 +393,8 @@ type Target union { ``` js ["/", "some-label"] -["bafkreiddwsbb7fasjb6k6jodakprtl4lhw6h3g4k7tew7vwwvd63veviie", "some-label"] -["bafkreiddwsbb7fasjb6k6jodakprtl4lhw6h3g4k7tew7vwwvd63veviie", "bafkreidlqsd6nh6hdgwhr4machsvusobpvn4qfrxfgl5vowoggzk2xpldq"] +[{"/": "bafkreiddwsbb7fasjb6k6jodakprtl4lhw6h3g4k7tew7vwwvd63veviie"}, "some-label"] +[{"/": "bafkreiddwsbb7fasjb6k6jodakprtl4lhw6h3g4k7tew7vwwvd63veviie"}, {"/": {"bytes": "bafkreidlqsd6nh6hdgwhr4machsvusobpvn4qfrxfgl5vowoggzk2xpldq"}}] ``` ## 5.2 Pipelining @@ -419,7 +433,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The { "ucan/invoke": { "v": "0.1.0", - "prf": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], + "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "nnc": "abcdef", "ext": null, "run": { @@ -472,7 +486,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The } } }, - "sig": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" + "sig": {"/": {"bytes": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA"}} } ``` @@ -521,7 +535,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The { "ucan/invoke": { "v": "0.1.0", - "inv": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], + "inv": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "nnc": "abcdef", "ext": null, "run": { @@ -537,13 +551,13 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The } } }, - "sig": "kQHtTruysx4S8SrvSjTwr6ttTLzc7dd7atANUYT-SRK6v_SX8bjHegWoDak2x6vTAZ6CcVKBt6JGpgnjABpsoL" + "sig": {"/": {"bytes": kQHtTruysx4S8SrvSjTwr6ttTLzc7dd7atANUYT-SRK6v_SX8bjHegWoDak2x6vTAZ6CcVKBt6JGpgnjABpsoL"}} } { "ucan/invoke": { "v": "0.1.0", - "prf": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], + "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "nnc": "12345", "ext": null, "run": { @@ -560,13 +574,13 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The } } }, - "sig": "XZRSmp5cHaXX6xWzSTxQqC95kQHtTruysx4S8SrvSjTwr6ttTLzc7dd7atANUQJXoWThUiVuCHWdMnQNQJgiJi" + "sig": {"/": {"bytes": "XZRSmp5cHaXX6xWzSTxQqC95kQHtTruysx4S8SrvSjTwr6ttTLzc7dd7atANUQJXoWThUiVuCHWdMnQNQJgiJi"}} } { "ucan/invoke": { "v": "0.1.0", - "prf": ["bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"], + "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "nnc": "02468", "ext": null, "run": { @@ -577,7 +591,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The { "to": "bob@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu", "update-dns"]} + "body": {"ucan/promise": [{"/": "bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu"}, "update-dns"]} } ] }, @@ -594,13 +608,13 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The "_": {"ucan/promise": ["/", "notify-bob"]} }, { - "_": {"ucan/promise": ["bafkreidcqdxosqave5u5pml3pyikiglozyscgqikvb6foppobtk3hwkjn4", "notify-carol"]} + "_": {"ucan/promise": [{"/": "bafkreidcqdxosqave5u5pml3pyikiglozyscgqikvb6foppobtk3hwkjn4"}, "notify-carol"]} } ] } } }, - "sig": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7" + "sig": {"/": {"bytes": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7"}} } ``` @@ -648,8 +662,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). -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 [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. @@ -657,3 +669,5 @@ Many thanks to [Quinn Wilton](https://github.com/QuinnWilton) for her discussion 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](https://github.com/matheus23/) for the enthusiastic feedback on the overall design and encoding. + +Thanks to [Christine Lemmer-Webber](https://github.com/cwebber) for the many conversations about capability systems and the programming models that they enable. From 2383f4dde6c03b27ee2e128ddfa4ba4a66da0ed2 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 10:57:02 -0800 Subject: [PATCH 086/154] Tighten up receipts, force varsig --- README.md | 49 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index cbe79e5c..c0a357d2 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ * [DAG-CBOR](https://ipld.io/specs/codecs/dag-cbor/spec/) * [UCAN](https://github.com/ucan-wg/spec/) * [UCAN-IPLD](https://github.com/ucan-wg/ucan-ipld/) +* [VarSig](https://github.com/ChainAgnostic/varsig/) # 0 Abstract @@ -124,6 +125,10 @@ The JSON examples below are given in [DAG-JSON](https://ipld.io/docs/codecs/know This format help disambiguate type information in generical 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 below MUST be signed with a [VarSig](https://github.com/ChainAgnostic/varsig/). + # 2 Roles Invocation adds two new roles to UCAN: invoker and executor. The existing UCAN delegator and delegate principals MUST persist to the invocation. @@ -162,7 +167,6 @@ The invocation wrapper MUST be signed by the same principal that issued the UCAN | `run` | `"*" or {String: {URI : {Ability : [Any]}}}` | | Which UCAN capabilities to run | Yes | | | `nnc` | `String` | | A unique nonce, to distinguish each invocation | Yes | | | `ext` | `Any` | | Non-normative extended fields | No | `null` | -| `sig` | `Bytes` | | Signature of the rest of the field canonicalized | Yes | | ### 3.1.1 Proofs @@ -194,8 +198,6 @@ The OPTIONAL `ext` field MAY contain arbitrary data. If not present, the `ext` f The `sig` field MUST contain the signature of the other fields. The signature MUST be given as a [VarSig](https://github.com/ChainAgnostic/varsig), which includes the payload encoding information. -If serialized as JSON, the `sig` field MUST be serialized as [unpadded base64url](https://datatracker.ietf.org/doc/html/rfc4648#section-5). - ## 3.2 IPLD Schema ``` ipldsch @@ -290,23 +292,38 @@ An invocation receipt is an attestation of the output of an invocation. A receip ## 4.1 Fields -| Field | Type | Description | Required | Default | -|-------|---------------|--------------------------------------------------------------------------------------------------|----------|---------| -| `inv` | `&Invocation` | CID of the Invocation that generated this response | Yes | | -| `rlt` | `{CID: Any}` | The results of each call, indexed by the CID of the `dag-cbor` encoded [Action](#32-ipld-schema) | Yes | | -| `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | -| `nnc` | `String` | A unique nonce, to distinguish each receipt | Yes | | -| `ext` | `Any` | Non-normative extended fields | No | `null` | -| `sig` | `Bytes` | Signature of the rest of the field canonicalized | Yes | | +| Field | Type | Description | Required | Default | +|-------|-----------------|----------------------------------------------------|----------|---------| +| `inv` | `&Invocation` | CID of the Invocation that generated this response | Yes | | +| `rlt` | `{String: Any}` | The results of each call, the action's label | Yes | | +| `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | +| `ext` | `Any` | Non-normative extended fields | No | `null` | + +### 4.1.1 Invocation + +The `inv` field MUST include a link to the Invocation that the Receipt is for. + +### 4.1.2 Result + +The `rlt` field MUST contain steps of the call graph, indexed by the action name inside the invocation. If the invocation is the implicit `"*"`, then the base64 hash of the concatenation of the URI, Ability and extensional fields MUST be used. -## 4.1 IPLD Schema +Results MAY omit any actions that have not yet completed, or whos results are not public. + +### 4.1.3 Version + +The `v` field MUST contain the Receipt schema version. + +### 4.1.4 Extended Fields + +The extended fields MAY be omitted or used to contain additional data about the reciept. This field MAY be used for tags, commentary, trace information, and so on. + +## 4.2 IPLD Schema ``` ipldsch type Receipt struct { inv &SignedInvocation - rlt {Hash : Any}} FIXME!!! + rlt {String : Any}} v SemVer - nnc String ext optional Any } @@ -314,11 +331,9 @@ type SignedReceipt struct { rec Receipt (rename "ucan/receipt") sig VarSig } - -type Hash Bytes ``` -## 4.2 JSON Example +## 4.3 JSON Example ``` json { From 3e9528904965a2ba90509712d76251106ebf5726 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 11:01:42 -0800 Subject: [PATCH 087/154] Remove Resultversion --- README.md | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index c0a357d2..67bb9cb3 100644 --- a/README.md +++ b/README.md @@ -290,14 +290,15 @@ An invocation receipt is an attestation of the output of an invocation. A receip **NB: a Receipt this does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. +Receipts don't have their own version field. Receipts MUST use the same version as the invocation that they contain. + ## 4.1 Fields -| Field | Type | Description | Required | Default | -|-------|-----------------|----------------------------------------------------|----------|---------| -| `inv` | `&Invocation` | CID of the Invocation that generated this response | Yes | | -| `rlt` | `{String: Any}` | The results of each call, the action's label | Yes | | -| `v` | `SemVer` | SemVer of the UCAN invocation object schema | Yes | | -| `ext` | `Any` | Non-normative extended fields | No | `null` | +| Field | Type | Description | Required | Default | +|--------|-----------------|----------------------------------------------------|----------|---------| +| `inv` | `&Invocation` | CID of the Invocation that generated this response | Yes | | +| `rlt` | `{String: Any}` | The results of each call, the action's label | Yes | | +| `meta` | `Any` | Non-normative extended fields | No | `null` | ### 4.1.1 Invocation @@ -309,22 +310,17 @@ The `rlt` field MUST contain steps of the call graph, indexed by the action name Results MAY omit any actions that have not yet completed, or whos results are not public. -### 4.1.3 Version - -The `v` field MUST contain the Receipt schema version. - -### 4.1.4 Extended Fields +### 4.1.3 Metadata Fields -The extended fields MAY be omitted or used to contain additional data about the reciept. This field MAY be used for tags, commentary, trace information, and so on. +The metadata field MAY be omitted or used to contain additional data about the reciept. This field MAY be used for tags, commentary, trace information, and so on. ## 4.2 IPLD Schema ``` ipldsch type Receipt struct { - inv &SignedInvocation - rlt {String : Any}} - v SemVer - ext optional Any + inv &SignedInvocation + rlt {String : Any}} + meta optional Any } type SignedReceipt struct { From fc527d09a5eb68d2998e3135fc2c8cc748e74f4e Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 11:37:23 -0800 Subject: [PATCH 088/154] Include subdelegations --- README.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 67bb9cb3..7102785b 100644 --- a/README.md +++ b/README.md @@ -294,11 +294,11 @@ Receipts don't have their own version field. Receipts MUST use the same version ## 4.1 Fields -| Field | Type | Description | Required | Default | -|--------|-----------------|----------------------------------------------------|----------|---------| -| `inv` | `&Invocation` | CID of the Invocation that generated this response | Yes | | -| `rlt` | `{String: Any}` | The results of each call, the action's label | Yes | | -| `meta` | `Any` | Non-normative extended fields | No | `null` | +| Field | Type | Description | Required | Default | +|--------|-----------------|-------------------------------------------------------------------------|----------|---------| +| `inv` | `&Invocation` | CID of the Invocation that generated this response | Yes | | +| `rlt` | `{String: Any}` | The results of each call, the action's label. MAY contain sub-receipts. | Yes | | +| `meta` | `Any` | Non-normative extended fields | No | `null` | ### 4.1.1 Invocation @@ -319,14 +319,19 @@ The metadata field MAY be omitted or used to contain additional data about the r ``` ipldsch type Receipt struct { inv &SignedInvocation - rlt {String : Any}} + rlt {String : Result}} meta optional Any } - + type SignedReceipt struct { rec Receipt (rename "ucan/receipt") sig VarSig } + +type Result union { + | Payload Any + | &SignedReceipt +} ``` ## 4.3 JSON Example @@ -353,10 +358,10 @@ type SignedReceipt struct { "body": "hello world" }, "ms": 476 - } - }, - "nnc": "xyz", - "ext": { + }, + "delegated-action": {"/": "bafkreieiupg4smeb5ammpsydbiea4yvwzwne5ly4hiripy4cjocqiat3ce"} + } + "meta": { "notes": "very receipt. such wow.", "tags": ["dag-house", "fission"] } From 9ef58386796ba14e2954c26e682a3cc878f33986 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 11:41:04 -0800 Subject: [PATCH 089/154] Typos --- .github/workflows/words-to-ignore.txt | 2 ++ README.md | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 67729f4e..dfd55dd2 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -6,6 +6,7 @@ CIDs Cap'N Cap'n CapTP +DAG-JSON ERTP Gozalishvili Hardt @@ -34,6 +35,7 @@ UCAN's UCANTO UCANs URI +VarSig Worthington Zelenka auth diff --git a/README.md b/README.md index 7102785b..ab82d90b 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ The JSON examples below are given in [DAG-JSON](https://ipld.io/docs/codecs/know {"/": {"bytes": "s0m3Byte5"}} ``` -This format help disambiguate type information in generical 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. +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 @@ -308,11 +308,11 @@ The `inv` field MUST include a link to the Invocation that the Receipt is for. The `rlt` field MUST contain steps of the call graph, indexed by the action name inside the invocation. If the invocation is the implicit `"*"`, then the base64 hash of the concatenation of the URI, Ability and extensional fields MUST be used. -Results MAY omit any actions that have not yet completed, or whos results are not public. +Results MAY omit any actions that have not yet completed, or results which are not public. ### 4.1.3 Metadata Fields -The metadata field MAY be omitted or used to contain additional data about the reciept. This field MAY be used for tags, commentary, trace information, and so on. +The 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. ## 4.2 IPLD Schema From 05369a7fa86f908c7f345bfc767a5eb3c124f3e5 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 11:42:44 -0800 Subject: [PATCH 090/154] Add to dictionary --- .github/workflows/words-to-ignore.txt | 2 ++ README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index dfd55dd2..dfda4f13 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -7,6 +7,7 @@ Cap'N Cap'n CapTP DAG-JSON +DAG_CBOR ERTP Gozalishvili Hardt @@ -32,6 +33,7 @@ Spritely TTL UCAN UCAN's +UCAN-IPLD UCANTO UCANs URI diff --git a/README.md b/README.md index ab82d90b..f5e20d62 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ As we shall see in the [discussion of promise pipelining](#5-promise-pipelining) ## 1.3 A Note On Serialization -The JSON examples below are given in [DAG-JSON](https://ipld.io/docs/codecs/known/dag-json/), but UCAN Invocation is actually defined as IPLD. This makes UCAN Invocation agnotsic to encoding. DAG-JSON follows particular conventions around wrapping CIDs and binary data in tags like so: +The JSON examples below are given in [DAG-JSON](https://ipld.io/docs/codecs/known/dag-json/), but UCAN Invocation is actually defined as IPLD. This makes UCAN Invocation agnostic to encoding. DAG-JSON follows particular conventions around wrapping CIDs and binary data in tags like so: ``` json // CID From 0f8680c4e6c6123dbdd6db747d2d8aa06224ee4f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 11:55:19 -0800 Subject: [PATCH 091/154] TYpo in dictonary --- .github/workflows/words-to-ignore.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index dfda4f13..62b48677 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -7,7 +7,7 @@ Cap'N Cap'n CapTP DAG-JSON -DAG_CBOR +DAG-CBOR ERTP Gozalishvili Hardt From f3652b65404c0012ca5067401fc3dda9e8f23a6f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 13:31:38 -0800 Subject: [PATCH 092/154] SemVer changes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f5e20d62..b8f25174 100644 --- a/README.md +++ b/README.md @@ -646,7 +646,7 @@ type Path = String type SemVer struct { num NumVer - tag String (implicit Null) + tag optional String } representation stringjoin { join "+" } From 88fbd37d240e5c415d69fbb128cf55d381f84f8f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 14:41:44 -0800 Subject: [PATCH 093/154] Code layout --- README.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index b8f25174..967f714c 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,11 @@ The `sig` field MUST contain the signature of the other fields. The signature MU ## 3.2 IPLD Schema ``` ipldsch +type SignedInvocation struct { + inv Invocation (rename "ucan/invoke") + sig VarSig -- Signature +} + type Invocation struct { prf [&UCAN] -- The UCANs providing authority v SemVer -- Version @@ -209,13 +214,6 @@ type Invocation struct { ext nullable Any (implicit null) -- Extended fields } -type SignedInvocation struct { - inv Invocation (rename "ucan/invoke") - sig VarSig -- Signature -} - -type VarSig Bytes - type Scope enum { | All ("*") | Action @@ -317,17 +315,17 @@ The metadata field MAY be omitted or used to contain additional data about the r ## 4.2 IPLD Schema ``` ipldsch -type Receipt struct { - inv &SignedInvocation - rlt {String : Result}} - meta optional Any -} - type SignedReceipt struct { rec Receipt (rename "ucan/receipt") sig VarSig } +type Receipt struct { + inv &SignedInvocation + rlt {String : Result} + meta Any (implicit Null) +} + type Result union { | Payload Any | &SignedReceipt @@ -639,10 +637,11 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The ## 6.1 Support Types ``` ipldsch -type CID = String -type URI = String -type Ability = String -type Path = String +type CID String +type URI String +type Ability String +type Path String +type VarSig Bytes type SemVer struct { num NumVer From 017b5a7ba0e41d5d67a84b8d8e64ed403084050b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 14:42:52 -0800 Subject: [PATCH 094/154] s/using/with/ --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 967f714c..4076ed80 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S UCAN is a chained-capability format. A UCAN contains all of the information that one would need to perform some action, 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 using 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 action. +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 action. ## 1.1 Intuition @@ -100,7 +100,7 @@ As we shall see in the [discussion of promise pipelining](#5-promise-pipelining) │ │ Dan ╞═══Read Email══►│ Erin │ │ │ │ │ ▲ │ │ │ │ └─────────┘ ┆ └─────────┘ │ -│ Using │ +│ With │ │ Result │ │ ┌─────────┐ Of ┌─────────┐ │ │ │ │ ┆ │ │ │ @@ -220,7 +220,7 @@ type Scope enum { } type Action enum { - using URI + with URI do Ability inputs Any } @@ -256,7 +256,7 @@ Promise pipelines are handled in more detail in [section 5](#5-promise-pipelinin "ext": null, "run": { "notify-bob": { - "using": "mailto://alice@example.com", + "with": "mailto://alice@example.com", "do": "msg/send", "inputs": [ { @@ -267,7 +267,7 @@ Promise pipelines are handled in more detail in [section 5](#5-promise-pipelinin ] }, "log-as-done": { - "using": "https://example.com/report" + "with": "https://example.com/report" "do": "crud/update" "inputs": { "from": "mailto://alice@exmaple.com", @@ -452,7 +452,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The "ext": null, "run": { "update-dns" : { - "using": "dns://example.com?TYPE=TXT": + "with": "dns://example.com?TYPE=TXT": "do": "crud/update", "inputs": { "value": "hello world", @@ -460,7 +460,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The } }, "notify-bob": { - "using": "mailto://alice@example.com", + "with": "mailto://alice@example.com", "do": "msg/send", "inputs": [ { @@ -471,7 +471,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The ] }, "notify-carol": { - "using": "mailto://alice@example.com", + "with": "mailto://alice@example.com", "do": "msg/send", "inputs": [ { @@ -482,7 +482,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The ] }, "log-as-done": { - "using": "https://example.com/report" + "with": "https://example.com/report" "do": "crud/update" "inputs": [ { @@ -554,7 +554,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The "ext": null, "run": { "update-dns": { - "using": "dns://example.com?TYPE=TXT", + "with": "dns://example.com?TYPE=TXT", "do": "crud/update" "inputs": [ { @@ -576,7 +576,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The "ext": null, "run": { "notify-carol": { - "using": "mailto://alice@example.com", + "with": "mailto://alice@example.com", "do": "msg/send", "inputs": [ { @@ -599,7 +599,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The "ext": null, "run": { "notify-bob": { - "using": "mailto://alice@example.com", + "with": "mailto://alice@example.com", "do": "msg/send", "inputs": [ { @@ -610,7 +610,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The ] }, "log-as-done": { - "using": "https://example.com/report" + "with": "https://example.com/report" "do": "crud/update" "inputs": [ { From d87a949c81273a4911edd9cdffef284bb8663792 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 15:10:15 -0800 Subject: [PATCH 095/154] rlt -> out everywhere --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4076ed80..ceefe92f 100644 --- a/README.md +++ b/README.md @@ -295,18 +295,18 @@ Receipts don't have their own version field. Receipts MUST use the same version | Field | Type | Description | Required | Default | |--------|-----------------|-------------------------------------------------------------------------|----------|---------| | `inv` | `&Invocation` | CID of the Invocation that generated this response | Yes | | -| `rlt` | `{String: Any}` | The results of each call, the action's label. MAY contain sub-receipts. | Yes | | +| `out` | `{String: Any}` | The results of each call, the action's label. MAY contain sub-receipts. | Yes | | | `meta` | `Any` | Non-normative extended fields | No | `null` | ### 4.1.1 Invocation The `inv` field MUST include a link to the Invocation that the Receipt is for. -### 4.1.2 Result +### 4.1.2 Output -The `rlt` field MUST contain steps of the call graph, indexed by the action name inside the invocation. If the invocation is the implicit `"*"`, then the base64 hash of the concatenation of the URI, Ability and extensional fields MUST be used. +The `out` field MUST contain the output of steps of the call graph, indexed by the action name inside the invocation. If the invocation is the implicit `"*"`, then the base64 hash of the concatenation of the URI, Ability and extensional fields MUST be used. -Results MAY omit any actions that have not yet completed, or results which are not public. +The `out` field MAY omit any actions that have not yet completed, or results which are not public. In this case, it is RECOMMENDED that ### 4.1.3 Metadata Fields @@ -322,7 +322,7 @@ type SignedReceipt struct { type Receipt struct { inv &SignedInvocation - rlt {String : Result} + out {String : Result} meta Any (implicit Null) } @@ -339,7 +339,7 @@ type Result union { "ucan/receipt": { "v": "0.1.0", "inv": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], - "rlt": { + "out": { "bafkreiakkqwuffzsxrzseo7fweeicqlnqanhvyffjieh3etsbnnkjxbphi": [ { "from": "alice@example.com", From ce3d25eef5b2b4331b081bcd2d8ec6a1d0cd7420 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 16:19:46 -0800 Subject: [PATCH 096/154] Remove CIDs --- README.md | 47 +++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index ceefe92f..05d5f1c5 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ The JSON examples below are given in [DAG-JSON](https://ipld.io/docs/codecs/know // CID {"/": Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD"} -// Bytes (e.g. signature) +// Bytes (e.g. a signature) {"/": {"bytes": "s0m3Byte5"}} ``` @@ -160,50 +160,41 @@ The invocation wrapper MUST be signed by the same principal that issued the UCAN ## 3.1 Fields -| Field | Type | Value | Description | Required | Default | -|-------|----------------------------------------------|-----------|---------------------------------------------------------|----------|---------| -| `v` | `SemVer` | `"0.1.0"` | SemVer of the UCAN invocation this schema | Yes | | -| `prf` | `[&UCAN]` | | The CIDs of the UCANs that provide the authority to run | Yes | | -| `run` | `"*" or {String: {URI : {Ability : [Any]}}}` | | Which UCAN capabilities to run | Yes | | -| `nnc` | `String` | | A unique nonce, to distinguish each invocation | Yes | | -| `ext` | `Any` | | Non-normative extended fields | No | `null` | - -### 3.1.1 Proofs - -The `prf` field MUST contain CIDs pointing to the UCANs that provide the authority to run these actions. The elements of this array MUST be sorted in ascending numeric order. Restricting the outmost UCANs to only the authority required for the current invocation is RECOMMENDED. - -### 3.1.2 Version +| Field | Type | Description | Required | Default | +|-------|------------------------------|---------------------------------------------------------|----------|---------| +| `v` | `SemVer` | SemVer of the UCAN invocation this schema (v0.1.0) | Yes | | +| `run` | `[CID] or {String : Action}` | Which UCAN capabilities to run | Yes | | +| `nnc` | `String` | A unique nonce, to distinguish each invocation | Yes | | +| `ext` | `Any` | Non-normative extended fields | No | `null` | + +### 3.1.1 Version The `v` field MUST contain the version of the invocation object schema. -### 3.1.3 Run Capabilities +### 3.1.2 Run Capabilities The `run` field MUST reference the actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](#5-promise-pipelining)) are intended to be run, then they MUST be prefixed with an arbitrary label and treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or greater authority. -#### 3.1.3.1 Promises +#### 3.1.2.1 Promises The only difference from general capabilities is that [promises](#5-promise-pipelining) MAY also be used as inputs to attenuated fields. If a capability input has the key `"_"` and the value is a promise, the input MUST be discarded and only used for determining sequencing actions. -### 3.1.4 Nonce +### 3.1.3 Nonce The `nnc` field MUST contain a unique nonce. This field exists to enable making the CID of each invocation unique. While this field MAY be an empty string, it is NOT RECOMMENDED. -### 3.1.5 Extended Fields +### 3.1.4 Extended Fields The OPTIONAL `ext` field MAY contain arbitrary data. If not present, the `ext` field MUST be interpreted as `null`, including for [signature](#315-signature). -### 3.1.6 Signature - -The `sig` field MUST contain the signature of the other fields. The signature MUST be given as a [VarSig](https://github.com/ChainAgnostic/varsig), which includes the payload encoding information. - ## 3.2 IPLD Schema ``` ipldsch type SignedInvocation struct { inv Invocation (rename "ucan/invoke") - sig VarSig -- Signature + sig VarSig } type Invocation struct { @@ -214,9 +205,9 @@ type Invocation struct { ext nullable Any (implicit null) -- Extended fields } -type Scope enum { - | All ("*") - | Action +type Scope union { + | [&UCAN] + | {String : Action} } type Action enum { @@ -236,10 +227,10 @@ type Action enum { "v": "0.1.0", "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "nnc": "abcdef", - "run": "*", // Explicitly "run all" + "run": [{"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}], "ext": null }, - "sig": { "/": { "bytes:": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" } } + "sig": {"/": { "bytes:": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" }} } ``` From c4c9be45b49bc0a0ccae94f161c9e6d07474b38e Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 16:22:12 -0800 Subject: [PATCH 097/154] Forgot the IPLD --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 05d5f1c5..d9f60aa6 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,6 @@ type SignedInvocation struct { } type Invocation struct { - prf [&UCAN] -- The UCANs providing authority v SemVer -- Version run Scope -- Which actions to invoke nnc String -- Nonce From bdabdf2870c26ac0f6931ff4bb029f1d8b81a895 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 16:23:15 -0800 Subject: [PATCH 098/154] update examples --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index d9f60aa6..fb08656e 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,6 @@ Promise pipelines are handled in more detail in [section 5](#5-promise-pipelinin { "ucan/invoke": { "v": "0.1.0", - "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "nnc": "02468", "ext": null, "run": { From 2522f78bf62f15d49121ba51831237db99e422af Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 16:23:50 -0800 Subject: [PATCH 099/154] Remove prf completely (woah) --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index fb08656e..60511102 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,6 @@ type Action enum { { "ucan/invoke": { "v": "0.1.0", - "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "nnc": "abcdef", "run": [{"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}], "ext": null @@ -436,7 +435,6 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The { "ucan/invoke": { "v": "0.1.0", - "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "nnc": "abcdef", "ext": null, "run": { @@ -560,7 +558,6 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The { "ucan/invoke": { "v": "0.1.0", - "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "nnc": "12345", "ext": null, "run": { @@ -583,7 +580,6 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The { "ucan/invoke": { "v": "0.1.0", - "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "nnc": "02468", "ext": null, "run": { From ce3ffb0512987190b5dfdc74033f4da59aaca9a2 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 16:40:31 -0800 Subject: [PATCH 100/154] Back it goes! --- README.md | 51 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 60511102..5fc7772c 100644 --- a/README.md +++ b/README.md @@ -160,32 +160,37 @@ The invocation wrapper MUST be signed by the same principal that issued the UCAN ## 3.1 Fields -| Field | Type | Description | Required | Default | -|-------|------------------------------|---------------------------------------------------------|----------|---------| -| `v` | `SemVer` | SemVer of the UCAN invocation this schema (v0.1.0) | Yes | | -| `run` | `[CID] or {String : Action}` | Which UCAN capabilities to run | Yes | | -| `nnc` | `String` | A unique nonce, to distinguish each invocation | Yes | | -| `ext` | `Any` | Non-normative extended fields | No | `null` | +| Field | Type | Description | Required | Default | +|-------|----------------------------|--------------------------------------------------------|----------|---------| +| `v` | `SemVer` | SemVer of the UCAN invocation this schema (v0.1.0) | Yes | | +| `prf` | `[&UCAN]` | UCANs that supply the authority to run this invocation | Yes | | +| `run` | `"*" or {String : Action}` | Which UCAN capabilities to run | Yes | | +| `nnc` | `String` | A unique nonce, to distinguish each invocation | Yes | | +| `ext` | `Any` | Non-normative extended fields | No | `null` | ### 3.1.1 Version The `v` field MUST contain the version of the invocation object schema. -### 3.1.2 Run Capabilities +### 3.1.2 + +The `prf` field MUST contain CIDs pointing to the UCANs that provide the authority to run these actions. The elements of this array MUST be sorted in ascending numeric order. Restricting the outmost UCANs to only the authority required for the current invocation is RECOMMENDED. + +### 3.1.3 Run Capabilities The `run` field MUST reference the actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](#5-promise-pipelining)) are intended to be run, then they MUST be prefixed with an arbitrary label and treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or greater authority. -#### 3.1.2.1 Promises +#### 3.1.3.1 Promises The only difference from general capabilities is that [promises](#5-promise-pipelining) MAY also be used as inputs to attenuated fields. If a capability input has the key `"_"` and the value is a promise, the input MUST be discarded and only used for determining sequencing actions. -### 3.1.3 Nonce +### 3.1.4 Nonce The `nnc` field MUST contain a unique nonce. This field exists to enable making the CID of each invocation unique. While this field MAY be an empty string, it is NOT RECOMMENDED. -### 3.1.4 Extended Fields +### 3.1.5 Extended Fields The OPTIONAL `ext` field MAY contain arbitrary data. If not present, the `ext` field MUST be interpreted as `null`, including for [signature](#315-signature). @@ -199,13 +204,14 @@ type SignedInvocation struct { type Invocation struct { v SemVer -- Version + prf [&UCAN] -- Authority to run this invocation run Scope -- Which actions to invoke nnc String -- Nonce ext nullable Any (implicit null) -- Extended fields } type Scope union { - | [&UCAN] + | All "*" | {String : Action} } @@ -224,8 +230,12 @@ type Action enum { { "ucan/invoke": { "v": "0.1.0", + "prf": [ + {"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}, + {"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"} + ], + "run": "*" "nnc": "abcdef", - "run": [{"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}], "ext": null }, "sig": {"/": { "bytes:": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" }} @@ -242,6 +252,10 @@ Promise pipelines are handled in more detail in [section 5](#5-promise-pipelinin "v": "0.1.0", "nnc": "02468", "ext": null, + "prf": [ + {"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}, + {"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"} + ], "run": { "notify-bob": { "with": "mailto://alice@example.com", @@ -436,7 +450,10 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The "ucan/invoke": { "v": "0.1.0", "nnc": "abcdef", - "ext": null, + "prf": [ + {"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}, + {"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"} + ], "run": { "update-dns" : { "with": "dns://example.com?TYPE=TXT": @@ -536,9 +553,8 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The { "ucan/invoke": { "v": "0.1.0", - "inv": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "nnc": "abcdef", - "ext": null, + "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "run": { "update-dns": { "with": "dns://example.com?TYPE=TXT", @@ -559,7 +575,10 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The "ucan/invoke": { "v": "0.1.0", "nnc": "12345", - "ext": null, + "prf": [ + {"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}, + {"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"} + ], "run": { "notify-carol": { "with": "mailto://alice@example.com", From 6717fcb3f9b721473a04f449bb2ad5e94dc1a998 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 16:41:33 -0800 Subject: [PATCH 101/154] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5fc7772c..12002f87 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ As we shall see in the [discussion of promise pipelining](#5-promise-pipelining) │ │ Dan ╞═══Read Email══►│ Erin │ │ │ │ │ ▲ │ │ │ │ └─────────┘ ┆ └─────────┘ │ -│ With │ +│ With │ │ Result │ │ ┌─────────┐ Of ┌─────────┐ │ │ │ │ ┆ │ │ │ From b157fad48fc983317f3598ba3f5217d99e346d74 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 21:17:31 -0800 Subject: [PATCH 102/154] A couple missing words --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 12002f87..33838258 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ The invocation wrapper MUST be signed by the same principal that issued the UCAN The `v` field MUST contain the version of the invocation object schema. -### 3.1.2 +### 3.1.2 Proofs The `prf` field MUST contain CIDs pointing to the UCANs that provide the authority to run these actions. The elements of this array MUST be sorted in ascending numeric order. Restricting the outmost UCANs to only the authority required for the current invocation is RECOMMENDED. @@ -215,10 +215,11 @@ type Scope union { | {String : Action} } -type Action enum { - with URI +type Action struct { + with URI do Ability inputs Any + meta {String : Any} } ``` From 8d137d432258c3160272b12d86330ee6eb7b0d8a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 21:26:15 -0800 Subject: [PATCH 103/154] s/action/task/g --- README.md | 78 +++++++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 33838258..dac2cbf8 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,9 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S > > — Anonymous -UCAN is a chained-capability format. A UCAN contains all of the information that one would need to perform some action, and the provable authority to do so. This begs the question: can UCAN be used directly as an RPC language? +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 action. +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 @@ -44,7 +44,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 an action 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](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. 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. @@ -64,9 +64,9 @@ However, there is clearly a distinction between passing a function and invoking ## 1.2 Separation of Concerns -Information about the scheduling, order, and pipelining of actions 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 action by the latest invoker. +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](#5-promise-pipelining), asking an agent to perform a sequence of actions 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](#5-promise-pipelining), 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). ``` ────────────────────────────────────────────Time──────────────────────────────────────────────────────► @@ -133,10 +133,10 @@ All payloads described below MUST be signed with a [VarSig](https://github.com/C Invocation 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 action (active) | -| `aud` | Delegate: gain authority (passive) | Executor: perform action (active) | +| UCAN Field | Delegation | Invocation | +|------------|----------------------------------------|---------------------------------| +| `iss` | Delegator: transfer authority (active) | Invoker: request task (active) | +| `aud` | Delegate: gain authority (passive) | Executor: perform task (active) | ## 2.1 Invoker @@ -152,7 +152,7 @@ The executor MUST be the UCAN delegate. Their DID MUST be set the in `aud` field # 3 Envelope -The invocation envelope is a thin wrapper around a UCAN that conveys that all of the contained UCAN actions SHOULD be performed. +The invocation envelope is a thin wrapper around a UCAN that conveys that all of the contained tasks SHOULD be performed. All of the roles from the referenced UCANs MUST persist to the invocation per the [roles table](#2-roles). @@ -160,13 +160,13 @@ The invocation wrapper MUST be signed by the same principal that issued the UCAN ## 3.1 Fields -| Field | Type | Description | Required | Default | -|-------|----------------------------|--------------------------------------------------------|----------|---------| -| `v` | `SemVer` | SemVer of the UCAN invocation this schema (v0.1.0) | Yes | | -| `prf` | `[&UCAN]` | UCANs that supply the authority to run this invocation | Yes | | -| `run` | `"*" or {String : Action}` | Which UCAN capabilities to run | Yes | | -| `nnc` | `String` | A unique nonce, to distinguish each invocation | Yes | | -| `ext` | `Any` | Non-normative extended fields | No | `null` | +| Field | Type | Description | Required | Default | +|-------|--------------------------|--------------------------------------------------------|----------|---------| +| `v` | `SemVer` | SemVer of the UCAN invocation this schema (v0.1.0) | Yes | | +| `prf` | `[&UCAN]` | UCANs that supply the authority to run this invocation | Yes | | +| `run` | `"*" or {String : Task}` | Which UCAN capabilities to run | Yes | | +| `nnc` | `String` | A unique nonce, to distinguish each invocation | Yes | | +| `ext` | `Any` | Non-normative extended fields | No | `null` | ### 3.1.1 Version @@ -174,17 +174,17 @@ The `v` field MUST contain the version of the invocation object schema. ### 3.1.2 Proofs -The `prf` field MUST contain CIDs pointing to the UCANs that provide the authority to run these actions. The elements of this array MUST be sorted in ascending numeric order. Restricting the outmost UCANs to only the authority required for the current invocation is RECOMMENDED. +The `prf` field MUST contain CIDs pointing to the UCANs that provide the authority to run these tasks. The elements of this array MUST be sorted in ascending numeric order. Restricting the outmost UCANs to only the authority required for the current invocation is RECOMMENDED. ### 3.1.3 Run Capabilities -The `run` field MUST reference the actions contained in the UCAN are to be run during the invocation. To run all actions in the underlying UCAN, the `"*"` value MUST be used. If only specific actions (or [pipelines](#5-promise-pipelining)) are intended to be run, then they MUST be prefixed with an arbitrary label and treated as a UCAN attenuation: all actions MUST be backed by a matching capability of equal or greater authority. +The `run` field MUST reference the tasks contained in the UCAN are to be run during the invocation. To run all tasks in the underlying UCAN, the `"*"` value MUST be used. If only specific tasks (or [pipelines](#5-promise-pipelining)) are intended to be run, then they MUST be prefixed with an arbitrary label and treated as a UCAN attenuation: all tasks MUST be backed by a matching capability of equal or greater authority. #### 3.1.3.1 Promises The only difference from general capabilities is that [promises](#5-promise-pipelining) MAY also be used as inputs to attenuated fields. -If a capability input has the key `"_"` and the value is a promise, the input MUST be discarded and only used for determining sequencing actions. +If a capability input has the key `"_"` and the value is a promise, the input MUST be discarded and only used for determining sequencing tasks. ### 3.1.4 Nonce @@ -205,17 +205,17 @@ type SignedInvocation struct { type Invocation struct { v SemVer -- Version prf [&UCAN] -- Authority to run this invocation - run Scope -- Which actions to invoke + run Scope -- Which tasks to invoke nnc String -- Nonce ext nullable Any (implicit null) -- Extended fields } type Scope union { | All "*" - | {String : Action} + | {String : Task} } -type Action struct { +type Task struct { with URI do Ability inputs Any @@ -245,7 +245,7 @@ type Action struct { ### 3.3.2 Promise Pipelines -Promise pipelines are handled in more detail in [section 5](#5-promise-pipelining). In brief, they enable taking the output of an action that has not yet run as the input to another action. Here is a simple example: +Promise pipelines are handled in more detail in [section 5](#5-promise-pipelining). In brief, they enable taking the output of a task that has not yet run as the input to another task. Here is a simple example: ``` js { @@ -295,11 +295,11 @@ Receipts don't have their own version field. Receipts MUST use the same version ## 4.1 Fields -| Field | Type | Description | Required | Default | -|--------|-----------------|-------------------------------------------------------------------------|----------|---------| -| `inv` | `&Invocation` | CID of the Invocation that generated this response | Yes | | -| `out` | `{String: Any}` | The results of each call, the action's label. MAY contain sub-receipts. | Yes | | -| `meta` | `Any` | Non-normative extended fields | No | `null` | +| Field | Type | Description | Required | Default | +|--------|-----------------|-----------------------------------------------------------------------|----------|---------| +| `inv` | `&Invocation` | CID of the Invocation that generated this response | Yes | | +| `out` | `{String: Any}` | The results of each call, the task's label. MAY contain sub-receipts. | Yes | | +| `meta` | `Any` | Non-normative extended fields | No | `null` | ### 4.1.1 Invocation @@ -307,9 +307,9 @@ The `inv` field MUST include a link to the Invocation that the Receipt is for. ### 4.1.2 Output -The `out` field MUST contain the output of steps of the call graph, indexed by the action name inside the invocation. If the invocation is the implicit `"*"`, then the base64 hash of the concatenation of the URI, Ability and extensional fields MUST be used. +The `out` field MUST contain the output of steps of the call graph, indexed by the task name inside the invocation. If the invocation is the implicit `"*"`, then the base64 hash of the concatenation of the URI, Ability and extensional fields MUST be used. -The `out` field MAY omit any actions that have not yet completed, or results which are not public. In this case, it is RECOMMENDED that +The `out` field MAY omit any tasks that have not yet completed, or results which are not public. In this case, it is RECOMMENDED that ### 4.1.3 Metadata Fields @@ -360,7 +360,7 @@ type Result union { }, "ms": 476 }, - "delegated-action": {"/": "bafkreieiupg4smeb5ammpsydbiea4yvwzwne5ly4hiripy4cjocqiat3ce"} + "delegated-task": {"/": "bafkreieiupg4smeb5ammpsydbiea4yvwzwne5ly4hiripy4cjocqiat3ce"} } "meta": { "notes": "very receipt. such wow.", @@ -377,18 +377,18 @@ type Result union { > > — [Mark Miller](https://github.com/erights), [Robust Composition](http://www.erights.org/talks/thesis/markm-thesis.pdf) -At UCAN creation time, the UCAN MAY not yet have all of the information required to construct the next request in a sequence. Waiting for each request to complete before proceeding to the next action 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 earlier invocations into the input of a later one. This liberates the invoker from waiting for each step. +At UCAN creation time, the UCAN MAY not yet have all of the information required to construct the next request in a sequence. 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 earlier invocations into the input of a later one. This liberates the invoker from waiting for each step. -The authority to execute later actions often cannot be fully attenuated in advance, since the executor controls the reported output of the prior step in a pipeline. When choosing to use pipelining, the invoker MUST delegate capabilities for any of the possible outputs. If tight control is required to limit authority, pipelining SHOULD NOT be used. +The authority to execute later task often cannot be fully attenuated in advance, since the executor controls the reported output of the prior step in a pipeline. When choosing to use pipelining, the invoker MUST delegate capabilities for any of the possible outputs. If tight control is required to limit authority, pipelining SHOULD NOT be used. ## 5.1 Promises ## 5.1.1 Fields -| Field | Type | Description | Required | Default | -|---------------|--------------|---------------------------------------------------------------------------------------|----------|----------| -| `promised` | `CID or "/"` | The Invocation being referenced | Yes | | -| `actionlabel` | `String` | The action's label. If the actions were implicit (`"run": "*"`), the the CID is used | Yes | | +| Field | Type | Description | Required | +|-------------|--------------|----------------------------------------------------------------------------------|----------| +| `promised` | `CID or "/"` | The Invocation being referenced | Yes | +| `taskLabel` | `String` | The task's label. If the tasks were implicit (`"run": "*"`), the the CID is used | Yes | The above table MUST be serialized as a tuple. In JSON, this SHOULD be represented as an array containing the values (but not keys) sequenced in the order they appear in the table. @@ -397,7 +397,7 @@ The above table MUST be serialized as a tuple. In JSON, this SHOULD be represent ``` ipldsch type Promise struct { promised Target - actionlabel String -- The label inside the invocation + taskLabel String -- The label inside the invocation } representation tuple type Target union { From c79253ceaa91b21e020decd6ff83a26ebfd7d439 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 22:00:15 -0800 Subject: [PATCH 104/154] Add missing commas in JSON --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index dac2cbf8..7d29582b 100644 --- a/README.md +++ b/README.md @@ -457,7 +457,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The ], "run": { "update-dns" : { - "with": "dns://example.com?TYPE=TXT": + "with": "dns://example.com?TYPE=TXT", "do": "crud/update", "inputs": { "value": "hello world", @@ -487,8 +487,8 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The ] }, "log-as-done": { - "with": "https://example.com/report" - "do": "crud/update" + "with": "https://example.com/report", + "do": "crud/update", "inputs": [ { "from": "mailto://alice@exmaple.com", @@ -559,7 +559,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The "run": { "update-dns": { "with": "dns://example.com?TYPE=TXT", - "do": "crud/update" + "do": "crud/update", "inputs": [ { "value": "hello world", @@ -615,8 +615,8 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The ] }, "log-as-done": { - "with": "https://example.com/report" - "do": "crud/update" + "with": "https://example.com/report", + "do": "crud/update", "inputs": [ { "from": "mailto://alice@exmaple.com", From 8b05002bac0d9a07f7673e45e71c6870a381e67f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 22:34:33 -0800 Subject: [PATCH 105/154] Thank rvagg --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7d29582b..3de62dfc 100644 --- a/README.md +++ b/README.md @@ -691,3 +691,5 @@ Thanks to [Blaine Cook](https://github.com/blaine) for sharing their experiences Thanks to [Philipp Krüger](https://github.com/matheus23/) for the enthusiastic feedback on the overall design and encoding. 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. From 392c7659bab5233e96588d83832ac4c02b08d778 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 23:37:45 -0800 Subject: [PATCH 106/154] Add to dict --- .github/workflows/words-to-ignore.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 62b48677..e1a8e9aa 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -1,13 +1,12 @@ -Acknowledgements -Agoric +AcknowleAgoric Akiko Bacalhau CIDs Cap'N Cap'n CapTP -DAG-JSON DAG-CBOR +DAG-JSON ERTP Gozalishvili Hardt @@ -37,6 +36,7 @@ UCAN-IPLD UCANTO UCANs URI +Vagg VarSig Worthington Zelenka @@ -47,9 +47,11 @@ canonicalized dataflow de delegator +dgements dholms expede facto +implicits interpretable invoker multiformat From 4134850c3fa442b62859408341ee31e2bf984670 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 29 Nov 2022 23:39:31 -0800 Subject: [PATCH 107/154] oops --- .github/workflows/words-to-ignore.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index e1a8e9aa..6908bc48 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -1,4 +1,5 @@ -AcknowleAgoric +Acknowledgements +Agoric Akiko Bacalhau CIDs @@ -47,7 +48,6 @@ canonicalized dataflow de delegator -dgements dholms expede facto From b10cae03050202a224b58f7967879a1a61d98b10 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 30 Nov 2022 21:19:59 -0800 Subject: [PATCH 108/154] Clearer success/error states --- README.md | 72 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 3de62dfc..f5d53c87 100644 --- a/README.md +++ b/README.md @@ -309,7 +309,9 @@ The `inv` field MUST include a link to the Invocation that the Receipt is for. The `out` field MUST contain the output of steps of the call graph, indexed by the task name inside the invocation. If the invocation is the implicit `"*"`, then the base64 hash of the concatenation of the URI, Ability and extensional fields MUST be used. -The `out` field MAY omit any tasks that have not yet completed, or results which are not public. In this case, it is RECOMMENDED that +The `out` field MAY omit any tasks that have not yet completed, or results which are not public. An `Invocation` may be associated to zero or more `Receipts`. + +A `Result` MAY include recursive `Receipt` CIDs in on the `Success` branch. As a Task may require subdelegation, the OPTIONAL `rec` field MAY be used to include recursive `Receipt`s. ### 4.1.3 Metadata Fields @@ -326,12 +328,22 @@ type SignedReceipt struct { type Receipt struct { inv &SignedInvocation out {String : Result} - meta Any (implicit Null) + meta optional Any } type Result union { - | Payload Any - | &SignedReceipt + | Success + | Failure +} + +type Failure struct { + err nullable String + trace Any +} + +type Success struct { + val Any + rec optional &SignedReceipt } ``` @@ -343,28 +355,30 @@ type Result union { "v": "0.1.0", "inv": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], "out": { - "bafkreiakkqwuffzsxrzseo7fweeicqlnqanhvyffjieh3etsbnnkjxbphi": [ - { - "from": "alice@example.com", - "text": "Hello world!" - }, - { - "from": "bob@example.com", - "text": "What's up?" - } - ], - "bafkreienxymjwglxlb3rkdyeyjt54ddoe4x4qi7a7hyfce3z56zspmy6pm": { - "http": { - "status": 200, - "body": "hello world" + "ok": { + "bafkreiakkqwuffzsxrzseo7fweeicqlnqanhvyffjieh3etsbnnkjxbphi": [ + { + "from": "alice@example.com", + "text": "Hello world!" + }, + { + "from": "bob@example.com", + "text": "What's up?" + } + ], + "bafkreienxymjwglxlb3rkdyeyjt54ddoe4x4qi7a7hyfce3z56zspmy6pm": { + "http": { + "status": 200, + "body": "hello world" + }, + "ms": 476 }, - "ms": 476 - }, - "delegated-task": {"/": "bafkreieiupg4smeb5ammpsydbiea4yvwzwne5ly4hiripy4cjocqiat3ce"} - } - "meta": { - "notes": "very receipt. such wow.", - "tags": ["dag-house", "fission"] + "delegated-task": {"/": "bafkreieiupg4smeb5ammpsydbiea4yvwzwne5ly4hiripy4cjocqiat3ce"} + } + "meta": { + "notes": "very receipt. such wow.", + "tags": ["dag-house", "fission"] + } } }, "sig": {"/": {"bytes": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt_VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA"}} @@ -381,6 +395,12 @@ At UCAN creation time, the UCAN MAY not yet have all of the information required The authority to execute later task often cannot be fully attenuated in advance, since the executor controls the reported output of the prior step in a pipeline. When choosing to use pipelining, the invoker MUST delegate capabilities for any of the possible outputs. If tight control is required to limit authority, pipelining SHOULD NOT be used. +Promises MUST resolve to a [`Result`](#42-ipld-schema). If a promise resolves to the `Success` branch, the value in the `val` MUST be extracted and substituted for the promise. Behaviour is left undefined if the promise returns on the `Failure` branch. + +Values MUST only be pipelined if they resolve to the `"ok"` branch of the `Result`. In the success case, the value inside the `"ok"` field MUST be extracted and replace the promise. + +A promise MAY be placed in any Task field. + ## 5.1 Promises ## 5.1.1 Fields @@ -408,7 +428,7 @@ type Target union { ## 5.1.3 JSON Examples -``` js +``` json ["/", "some-label"] [{"/": "bafkreiddwsbb7fasjb6k6jodakprtl4lhw6h3g4k7tew7vwwvd63veviie"}, "some-label"] [{"/": "bafkreiddwsbb7fasjb6k6jodakprtl4lhw6h3g4k7tew7vwwvd63veviie"}, {"/": {"bytes": "bafkreidlqsd6nh6hdgwhr4machsvusobpvn4qfrxfgl5vowoggzk2xpldq"}}] From e5639daa9ae1ce5ab1e04901f3e94434cda6eb50 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 30 Nov 2022 21:26:31 -0800 Subject: [PATCH 109/154] Add warning label --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f5d53c87..4a3e5ffb 100644 --- a/README.md +++ b/README.md @@ -399,10 +399,10 @@ Promises MUST resolve to a [`Result`](#42-ipld-schema). If a promise resolves to Values MUST only be pipelined if they resolve to the `"ok"` branch of the `Result`. In the success case, the value inside the `"ok"` field MUST be extracted and replace the promise. -A promise MAY be placed in any Task field. +A promise MAY be placed in any Task field. Substituting into the `with` and `do` fields is NOT RECOMMENDED in fully trustless contexts, as it makes it difficult to understand what is involved in the invocation in advance. ## 5.1 Promises - + ## 5.1.1 Fields | Field | Type | Description | Required | From 4ec759d7e3ee606597d18e3c4abc4d9c017b96f9 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 30 Nov 2022 21:52:29 -0800 Subject: [PATCH 110/154] Add word to dict --- .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 6908bc48..049c567f 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -64,6 +64,7 @@ requestor's responder signalling simple-but-evolvable +subdelegation trustless ucanto unpadded diff --git a/README.md b/README.md index 4a3e5ffb..070a6b7b 100644 --- a/README.md +++ b/README.md @@ -395,7 +395,7 @@ At UCAN creation time, the UCAN MAY not yet have all of the information required The authority to execute later task often cannot be fully attenuated in advance, since the executor controls the reported output of the prior step in a pipeline. When choosing to use pipelining, the invoker MUST delegate capabilities for any of the possible outputs. If tight control is required to limit authority, pipelining SHOULD NOT be used. -Promises MUST resolve to a [`Result`](#42-ipld-schema). If a promise resolves to the `Success` branch, the value in the `val` MUST be extracted and substituted for the promise. Behaviour is left undefined if the promise returns on the `Failure` branch. +Promises MUST resolve to a [`Result`](#42-ipld-schema). If a promise resolves to the `Success` branch, the value in the `val` MUST be extracted and substituted for the promise. Behavior is left undefined if the promise returns on the `Failure` branch. Values MUST only be pipelined if they resolve to the `"ok"` branch of the `Result`. In the success case, the value inside the `"ok"` field MUST be extracted and replace the promise. From 6cda9b218c2b2076be5755a127f8dd4adf35a048 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 30 Nov 2022 23:17:52 -0800 Subject: [PATCH 111/154] Typo in JSON --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 070a6b7b..8c97e93e 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ The JSON examples below are given in [DAG-JSON](https://ipld.io/docs/codecs/know ``` json // CID -{"/": Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD"} +{"/": "Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD"} // Bytes (e.g. a signature) {"/": {"bytes": "s0m3Byte5"}} From 41eb8e6a68827bb256a364fc23babe006976ef76 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 2 Dec 2022 13:20:40 -0800 Subject: [PATCH 112/154] Consistent "Varsig" --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8c97e93e..d870d054 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ * [DAG-CBOR](https://ipld.io/specs/codecs/dag-cbor/spec/) * [UCAN](https://github.com/ucan-wg/spec/) * [UCAN-IPLD](https://github.com/ucan-wg/ucan-ipld/) -* [VarSig](https://github.com/ChainAgnostic/varsig/) +* [Varsig](https://github.com/ChainAgnostic/varsig/) # 0 Abstract @@ -127,7 +127,7 @@ This format help disambiguate type information in generic DAG-JSON tooling. Howe ## 1.4 Signatures -All payloads described below MUST be signed with a [VarSig](https://github.com/ChainAgnostic/varsig/). +All payloads described below MUST be signed with a [Varsig](https://github.com/ChainAgnostic/varsig/). # 2 Roles @@ -199,7 +199,7 @@ The OPTIONAL `ext` field MAY contain arbitrary data. If not present, the `ext` f ``` ipldsch type SignedInvocation struct { inv Invocation (rename "ucan/invoke") - sig VarSig + sig Varsig } type Invocation struct { @@ -322,7 +322,7 @@ The metadata field MAY be omitted or used to contain additional data about the r ``` ipldsch type SignedReceipt struct { rec Receipt (rename "ucan/receipt") - sig VarSig + sig Varsig } type Receipt struct { @@ -666,7 +666,7 @@ type CID String type URI String type Ability String type Path String -type VarSig Bytes +type Varsig Bytes type SemVer struct { num NumVer From f1de75e304854c3ba223c96a20268d65f348c106 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 2 Dec 2022 13:21:00 -0800 Subject: [PATCH 113/154] Update Varsig spelling in dict --- .github/workflows/words-to-ignore.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 049c567f..378d95e9 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -38,7 +38,7 @@ UCANTO UCANs URI Vagg -VarSig +Varsig Worthington Zelenka auth From 7e0bedba94bc2633bfd8082a91e96cf814bac858 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 3 Dec 2022 19:48:45 +0000 Subject: [PATCH 114/154] Update README.md Co-authored-by: Irakli Gozalishvili Signed-off-by: Brooklyn Zelenka --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d870d054..5806cd1f 100644 --- a/README.md +++ b/README.md @@ -297,7 +297,7 @@ Receipts don't have their own version field. Receipts MUST use the same version | Field | Type | Description | Required | Default | |--------|-----------------|-----------------------------------------------------------------------|----------|---------| -| `inv` | `&Invocation` | CID of the Invocation that generated this response | Yes | | +| `inv` | `&SignedInvocation` | CID of the Invocation that generated this response | Yes | | | `out` | `{String: Any}` | The results of each call, the task's label. MAY contain sub-receipts. | Yes | | | `meta` | `Any` | Non-normative extended fields | No | `null` | From 0f6942ff10dfbe59fb9843d0da2cacc99907e640 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 3 Dec 2022 13:04:03 -0800 Subject: [PATCH 115/154] Add Graphviz action --- README.md | 90 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 5806cd1f..3c28a089 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ However, there is clearly a distinction between passing a function and invoking 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](#5-promise-pipelining), 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](#6-promise-pipelining), 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). ``` ────────────────────────────────────────────Time──────────────────────────────────────────────────────► @@ -178,11 +178,11 @@ The `prf` field MUST contain CIDs pointing to the UCANs that provide the authori ### 3.1.3 Run Capabilities -The `run` field MUST reference the tasks contained in the UCAN are to be run during the invocation. To run all tasks in the underlying UCAN, the `"*"` value MUST be used. If only specific tasks (or [pipelines](#5-promise-pipelining)) are intended to be run, then they MUST be prefixed with an arbitrary label and treated as a UCAN attenuation: all tasks MUST be backed by a matching capability of equal or greater authority. +The `run` field MUST reference the tasks contained in the UCAN are to be run during the invocation. To run all tasks in the underlying UCAN, the `"*"` value MUST be used. If only specific tasks (or [pipelines](#6-promise-pipelining)) are intended to be run, then they MUST be prefixed with an arbitrary label and treated as a UCAN attenuation: all tasks MUST be backed by a matching capability of equal or greater authority. #### 3.1.3.1 Promises -The only difference from general capabilities is that [promises](#5-promise-pipelining) MAY also be used as inputs to attenuated fields. +The only difference from general capabilities is that [promises](#6-promise-pipelining) MAY also be used as inputs to attenuated fields. If a capability input has the key `"_"` and the value is a promise, the input MUST be discarded and only used for determining sequencing tasks. @@ -245,7 +245,7 @@ type Task struct { ### 3.3.2 Promise Pipelines -Promise pipelines are handled in more detail in [section 5](#5-promise-pipelining). In brief, they enable taking the output of a task that has not yet run as the input to another task. Here is a simple example: +Promise pipelines are handled in more detail in [§6](#6-promise-pipelining). In brief, they enable taking the output of a task that has not yet run as the input to another task. Here is a simple example: ``` js { @@ -295,11 +295,11 @@ Receipts don't have their own version field. Receipts MUST use the same version ## 4.1 Fields -| Field | Type | Description | Required | Default | -|--------|-----------------|-----------------------------------------------------------------------|----------|---------| -| `inv` | `&SignedInvocation` | CID of the Invocation that generated this response | Yes | | -| `out` | `{String: Any}` | The results of each call, the task's label. MAY contain sub-receipts. | Yes | | -| `meta` | `Any` | Non-normative extended fields | No | `null` | +| Field | Type | Description | Required | Default | +|--------|----------------------|-----------------------------------------------------------------------|----------|---------| +| `inv` | `&SignedInvocation` | CID of the Invocation that generated this response | Yes | | +| `out` | `{String : Receipt}` | The results of each call, the task's label. MAY contain sub-receipts. | Yes | | +| `meta` | `{String : Any}` | Non-normative extended fields | No | `null` | ### 4.1.1 Invocation @@ -385,7 +385,35 @@ type Success struct { } ``` -# 5 Promise Pipelining +# 5 Pointers + +Invocation pointers identify a specific task inside a specific invocation. Since tasks may look identical across calls, they MUST be scoped to a specific [`SignedInvocation`](#32-ipld-schema). + +## 5.1 Fields + +## 5.2 IPLD Schema + +``` ipldsch +type TaskPointer struct { + inv InvocationPointer + taskLabel String +} representation tuple + +type InvocationPointer union { + | Local "/" + | &SignedInvocation +} +``` + +## 5.3 JSON Examples + +``` json +["/", "some-label"] +[{"/": "bafkreiddwsbb7fasjb6k6jodakprtl4lhw6h3g4k7tew7vwwvd63veviie"}, "some-label"] +[{"/": "bafkreiddwsbb7fasjb6k6jodakprtl4lhw6h3g4k7tew7vwwvd63veviie"}, {"/": {"bytes": "bafkreidlqsd6nh6hdgwhr4machsvusobpvn4qfrxfgl5vowoggzk2xpldq"}}] +``` + +# 6 Promise Pipelining > 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 > @@ -401,40 +429,28 @@ Values MUST only be pipelined if they resolve to the `"ok"` branch of the `Resul A promise MAY be placed in any Task field. Substituting into the `with` and `do` fields is NOT RECOMMENDED in fully trustless contexts, as it makes it difficult to understand what is involved in the invocation in advance. -## 5.1 Promises +## 6.1 Promises -## 5.1.1 Fields +## 6.1.1 Fields -| Field | Type | Description | Required | -|-------------|--------------|----------------------------------------------------------------------------------|----------| -| `promised` | `CID or "/"` | The Invocation being referenced | Yes | -| `taskLabel` | `String` | The task's label. If the tasks were implicit (`"run": "*"`), the the CID is used | Yes | +| Field | Type | Description | Required | +|----------------|---------------------|---------------------------------|----------| +| `ucan/promise` | `InvocationPointer` | The Invocation being referenced | Yes | The above table MUST be serialized as a tuple. In JSON, this SHOULD be represented as an array containing the values (but not keys) sequenced in the order they appear in the table. -## 5.1.2 IPLD Schema +## 6.1.2 IPLD Schema ``` ipldsch type Promise struct { - promised Target - taskLabel String -- The label inside the invocation -} representation tuple - -type Target union { - | Local "/" - | &Invocation + promised TaskPointer (rename "ucan/") } ``` -## 5.1.3 JSON Examples +## 6.1.3 JSON Examples -``` json -["/", "some-label"] -[{"/": "bafkreiddwsbb7fasjb6k6jodakprtl4lhw6h3g4k7tew7vwwvd63veviie"}, "some-label"] -[{"/": "bafkreiddwsbb7fasjb6k6jodakprtl4lhw6h3g4k7tew7vwwvd63veviie"}, {"/": {"bytes": "bafkreidlqsd6nh6hdgwhr4machsvusobpvn4qfrxfgl5vowoggzk2xpldq"}}] -``` -## 5.2 Pipelining +## 6.2 Pipelining Pipelining uses promises as inputs to determine the required dataflow graph. The following examples both express the following dataflow graph: @@ -464,7 +480,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The └────────────────────────────┘ ``` -#### 5.2.1 Batched +#### 6.2.1 Batched ``` json { @@ -529,7 +545,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The } ``` -### 5.2.2 Serial Pipeline +### 6.2.2 Serial Pipeline ``` ┌────────────────────────────┐ @@ -657,9 +673,9 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The } ``` -# 6 Appendix +# 7 Appendix -## 6.1 Support Types +## 7.1 Support Types ``` ipldsch type CID String @@ -684,7 +700,7 @@ type NumVer struct { } ``` -# 7 Prior Art +# 8 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. @@ -696,7 +712,7 @@ The [Object Capability Network (OCapN)](https://github.com/ocapn/ocapn) protocol [Cap 'n Proto RPC](https://capnproto.org/) is an influential RPC framework [based on concepts from CapTP](https://capnproto.org/rpc.html#specification). -# 8 Acknowledgements +# 9 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/). From c9a3345f60608928d976d711c8ad48be1a662550 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 3 Dec 2022 13:04:18 -0800 Subject: [PATCH 116/154] Okay but actually this time --- .github/workflows/dot.yml | 16 ++++++++++++++++ diagrams/concepts.dot | 11 +++++++++++ 2 files changed, 27 insertions(+) create mode 100644 .github/workflows/dot.yml create mode 100644 diagrams/concepts.dot diff --git a/.github/workflows/dot.yml b/.github/workflows/dot.yml new file mode 100644 index 00000000..9f2b9ec8 --- /dev/null +++ b/.github/workflows/dot.yml @@ -0,0 +1,16 @@ +on: + pull_request: + paths: + - "**.dot" + +jobs: + digraph_to_svg_job: + runs-on: ubuntu-latest + name: Create svg image from digraph .dot file + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + - name: Digraph to SVG step + id: digraph_to_svg + uses: alexrothenberg/digraph-to-svg-action@master diff --git a/diagrams/concepts.dot b/diagrams/concepts.dot new file mode 100644 index 00000000..06c640a5 --- /dev/null +++ b/diagrams/concepts.dot @@ -0,0 +1,11 @@ +digraph { + rankdir="LR" + + node [ + style=rounded + shape=box + ] + + Invocation -> Pointer -> Receipt -> Promise -> Pipeline + Invocation -> Batch -> Pipeline +} From 6c75a60bfb1a0ff4311f1dba01f7e4b3b6117597 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 3 Dec 2022 21:04:58 +0000 Subject: [PATCH 117/154] action create .svg from .dot file --- diagrams/concepts.svg | 84 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 diagrams/concepts.svg diff --git a/diagrams/concepts.svg b/diagrams/concepts.svg new file mode 100644 index 00000000..e9eb037a --- /dev/null +++ b/diagrams/concepts.svg @@ -0,0 +1,84 @@ + + + + + + + + + +Invocation + +Invocation + + + +Pointer + +Pointer + + + +Invocation->Pointer + + + + + +Batch + +Batch + + + +Invocation->Batch + + + + + +Receipt + +Receipt + + + +Pointer->Receipt + + + + + +Promise + +Promise + + + +Receipt->Promise + + + + + +Pipeline + +Pipeline + + + +Promise->Pipeline + + + + + +Batch->Pipeline + + + + + From 4029ea29406d928dae4cae1932ca1a6f764799f3 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 3 Dec 2022 13:08:47 -0800 Subject: [PATCH 118/154] Add diagram to README --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3c28a089..c1c9b7b4 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,9 @@ This format help disambiguate type information in generic DAG-JSON tooling. Howe All payloads described below MUST be signed with a [Varsig](https://github.com/ChainAgnostic/varsig/). -# 2 Roles +# 2 High-Level Concepts + +## 2.1 Roles Invocation adds two new roles to UCAN: invoker and executor. The existing UCAN delegator and delegate principals MUST persist to the invocation. @@ -138,18 +140,22 @@ Invocation adds two new roles to UCAN: invoker and executor. The existing UCAN d | `iss` | Delegator: transfer authority (active) | Invoker: request task (active) | | `aud` | Delegate: gain authority (passive) | Executor: perform task (active) | -## 2.1 Invoker +### 2.1.1 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.2 Executor +### 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 + +![](./diagrams/concepts.svg) + # 3 Envelope The invocation envelope is a thin wrapper around a UCAN that conveys that all of the contained tasks SHOULD be performed. From f954ac53aea40dcc83eb848cf4075da6f48ecfd4 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Dec 2022 06:34:32 +0000 Subject: [PATCH 119/154] Major cleanup / reorg (#4) --- .clabot | 1 + .github/workflows/words-to-ignore.txt | 13 + README.md | 1341 +++++++++++++++++-------- diagrams/batch-pipeline.dot | 14 + diagrams/batch-pipeline.svg | 66 ++ diagrams/concepts.dot | 18 +- diagrams/concepts.svg | 147 ++- diagrams/serial-pipeline.dot | 31 + diagrams/serial-pipeline.svg | 77 ++ 9 files changed, 1240 insertions(+), 468 deletions(-) create mode 100644 diagrams/batch-pipeline.dot create mode 100644 diagrams/batch-pipeline.svg create mode 100644 diagrams/serial-pipeline.dot create mode 100644 diagrams/serial-pipeline.svg diff --git a/.clabot b/.clabot index 13165deb..5d6b5d93 100644 --- a/.clabot +++ b/.clabot @@ -1,5 +1,6 @@ { "contributors": [ + "actions-user", "expede" ] } diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 378d95e9..81fa1acf 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -1,6 +1,7 @@ Acknowledgements Agoric Akiko +Akiko's Bacalhau CIDs Cap'N @@ -14,12 +15,15 @@ Hardt Holmgren IPLD IPVM +Invoker's +Invokers Irakli JSON JSONPath JWT JWT-encoded Krüger +Lakhani Lemmer-Webber Marsen OAuth @@ -37,14 +41,18 @@ UCAN-IPLD UCANTO UCANs URI +URIs Vagg Varsig +WebAssembly Worthington +Zeeshan Zelenka auth backend base64url canonicalized +cryptographically dataflow de delegator @@ -53,6 +61,7 @@ expede facto implicits interpretable +invariants invoker multiformat outmost @@ -64,9 +73,13 @@ requestor's responder signalling simple-but-evolvable +struct +subdelegated subdelegation +subtype trustless ucanto +unlabelled unpadded url v0 diff --git a/README.md b/README.md index c1c9b7b4..b757d772 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# UCAN Invocation Specification v0.1.0 +# UCAN Task Specification v0.1.0 ## Editors @@ -18,7 +18,7 @@ # 0 Abstract -UCAN Invocation defines a format for expressing the intention to run delegated capabilities from a UCAN, and how to promise pipeline invocations. +UCAN Invocation defines a format for expressing the intention to run delegated capabilities from a UCAN, the attested receipts from an execution, and how to extend computation via promise pipelining. ## Language @@ -40,7 +40,7 @@ Some teams have had success with UCAN directly for RPC when the intention is cle 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 Alice'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. +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. ## 1.1.2 Lazy vs Eager Evaluation @@ -54,7 +54,7 @@ message // Nothing happens message() // A message interups the user ``` -Delegating a capability is like the statement `message`. Invocation is akin to `message()`. It's true that sometimes we know to run things from their surrounding context without the parentheses: +Delegating a capability is like the statement `message`. Task is akin to `message()`. It's true that sometimes we know to run things from their surrounding context without the parentheses: ```js [1,2,3].map(message) // Message runs 3 times @@ -113,7 +113,7 @@ As we shall see in the [discussion of promise pipelining](#6-promise-pipelining) ## 1.3 A Note On Serialization -The JSON examples below are given in [DAG-JSON](https://ipld.io/docs/codecs/known/dag-json/), but UCAN Invocation is actually defined as IPLD. This makes UCAN Invocation agnostic to encoding. DAG-JSON follows particular conventions around wrapping CIDs and binary data in tags like so: +The JSON examples below are given in [DAG-JSON](https://ipld.io/docs/codecs/known/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: ``` json // CID @@ -127,15 +127,15 @@ This format help disambiguate type information in generic DAG-JSON tooling. Howe ## 1.4 Signatures -All payloads described below MUST be signed with a [Varsig](https://github.com/ChainAgnostic/varsig/). +All payloads described in this spec MUST be signed with a [Varsig](https://github.com/ChainAgnostic/varsig/). # 2 High-Level Concepts ## 2.1 Roles -Invocation adds two new roles to UCAN: invoker and executor. The existing UCAN delegator and delegate principals MUST persist to the invocation. +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 | +| UCAN Field | Delegation | Task | |------------|----------------------------------------|---------------------------------| | `iss` | Delegator: transfer authority (active) | Invoker: request task (active) | | `aud` | Delegate: gain authority (passive) | Executor: perform task (active) | @@ -156,393 +156,996 @@ The executor MUST be the UCAN delegate. Their DID MUST be set the in `aud` field ![](./diagrams/concepts.svg) -# 3 Envelope +### 2.2.1 Closure -The invocation envelope is a thin wrapper around a UCAN that conveys that all of the contained tasks SHOULD be performed. +A [Closure](#3-closure) is like a deferred function application: a request to perform some action on a resource with specific inputs. -All of the roles from the referenced UCANs MUST persist to the invocation per the [roles table](#2-roles). +### 2.2.2 Task -The invocation wrapper MUST be signed by the same principal that issued the UCAN. +A [Task](#4-task) extends a Closure with additional metadata that is not used to describe the meaning of the computation or effect to be run. -## 3.1 Fields +### 2.2.3 Batch -| Field | Type | Description | Required | Default | -|-------|--------------------------|--------------------------------------------------------|----------|---------| -| `v` | `SemVer` | SemVer of the UCAN invocation this schema (v0.1.0) | Yes | | -| `prf` | `[&UCAN]` | UCANs that supply the authority to run this invocation | Yes | | -| `run` | `"*" or {String : Task}` | Which UCAN capabilities to run | Yes | | -| `nnc` | `String` | A unique nonce, to distinguish each invocation | Yes | | -| `ext` | `Any` | Non-normative extended fields | No | `null` | - -### 3.1.1 Version +A [Batch](#5-batch) is a way of requesting more than one action at once. -The `v` field MUST contain the version of the invocation object schema. +### 2.2.4 Invocation -### 3.1.2 Proofs +An [Invocation](#6-invocation) is the cryptographically signed container for a Batch. This is the step that "forces" the "deferred" Closure. -The `prf` field MUST contain CIDs pointing to the UCANs that provide the authority to run these tasks. The elements of this array MUST be sorted in ascending numeric order. Restricting the outmost UCANs to only the authority required for the current invocation is RECOMMENDED. +### 2.2.5 Pointers -### 3.1.3 Run Capabilities +An [Invocation Pointer](#7-pointer) identifies a specific invocation. An Invoked Task Pointer points to a unique Task inside an Invocation. -The `run` field MUST reference the tasks contained in the UCAN are to be run during the invocation. To run all tasks in the underlying UCAN, the `"*"` value MUST be used. If only specific tasks (or [pipelines](#6-promise-pipelining)) are intended to be run, then they MUST be prefixed with an arbitrary label and treated as a UCAN attenuation: all tasks MUST be backed by a matching capability of equal or greater authority. +### 2.2.6 Result -#### 3.1.3.1 Promises +A [Result](#8-result) is the output of a Closure. -The only difference from general capabilities is that [promises](#6-promise-pipelining) MAY also be used as inputs to attenuated fields. +### 2.2.7 Receipt -If a capability input has the key `"_"` and the value is a promise, the input MUST be discarded and only used for determining sequencing tasks. +A [Receipt](#9-receipt) describes the output of an invocation, referenced by its Invocation Pointer. -### 3.1.4 Nonce +### 2.2.8 Promise -The `nnc` field MUST contain a unique nonce. This field exists to enable making the CID of each invocation unique. While this field MAY be an empty string, it is NOT RECOMMENDED. +A [promise](#10-promise) is a reference to the receipt of an action that has yet to return a receipt. -### 3.1.5 Extended Fields +## 2.3 IPLD Schema -The OPTIONAL `ext` field MAY contain arbitrary data. If not present, the `ext` field MUST be interpreted as `null`, including for [signature](#315-signature). +``` ipldsch +type Closure struct { + with URI + do Ability + inputs Any +} -## 3.2 IPLD Schema +type Task struct { + with URI + do Ability + inputs Any + meta {String : Any} (implicit {}) +} -``` ipldsch -type SignedInvocation struct { - inv Invocation (rename "ucan/invoke") - sig Varsig +type Batch union { + | Named {String : Task} + | List [Task] } type Invocation struct { - v SemVer -- Version - prf [&UCAN] -- Authority to run this invocation - run Scope -- Which tasks to invoke - nnc String -- Nonce - ext nullable Any (implicit null) -- Extended fields + uiv SemVer + run Batch + prf [&UCAN] + nnc String + meta {String : Any} (implicit {}) + sig Varsig +} + +type InvocationPointer union { + | "/" -- Relative to the current invocation + | &TaskInvocation +} + +type InvokedTaskPointer struct { + envl InvPtr + label String +} representation tuple + +type Receipt struct { + ran &InvocationPointer + out {String : Result} + rec {String : &Receipt} + meta {String : Any} + sig Varsig +} + +type Result union { + | Any ("ok") -- Success + | Any ("err") -- Failure +} representation keyed + +type Promise union { + | InvokedTaskPointer "ucan/ok" + | InvokedTaskPointer "ucan/err" + | InvokedTaskPointer "ucan/ptr" +} representation keyed +``` + +# 3 Closure + +A Closure is the smallest unit of work that can be requested from a UCAN. It describes one `(resource, ability, inputs)` triple. The `inputs` field is free form, and depend on the specific resource and ability being interacted with, and not described in this specification. + +Using the JavaScript analogy from the introduction, a Closure is similar to wrapping a call in an anonymous function: + +``` json +{ + "with": "mailto://alice@example.com", + "do": "msg/send", + "inputs": { + "to": ["bob@example.com", "carol@example.com"], + "subject": "hello", + "body": "world" + } +} +``` + +``` js +// Pseudocode +() => msg.send("mailto:alice@example.com", { + to: ["bob@example.com", "carol@example.com"], + subject: "hello", + body: "world" +}) +``` + +Later, when we explore [Promises](# 10-promise), this also includes capturing the promise: + +``` json +{ + "mailingList": { + "with": "https://example.com/mailinglist", + "do": "crud/read" + }, + "sendEmail": { + "with": "mailto://alice@example.com", + "do": "msg/send", + "inputs": { + "to": {"ucan/promise": ["/", "get-mailing-list"]} + "subject": "hello", + "body": "world" + } + } +} +``` + +``` js +// Pseudocode +const mailingList = crud.read("https://exmaple.com/mailinglist", {}) // ---┐ + // │ +const sendEmail = () => msg.send("mailto:alice@example.com", { // │ + to: mailingList, // <----------------------------------------------------┘ + subject: "hello", + body: "world" +}) +``` + +## 3.1 Fields + +``` ipldsch +type Closure struct { + with URI + do Ability + inputs Any +} +``` + +### 3.1.1 Resource + +The `with` 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.1.2 Ability + +The `do` 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.1.3 Inputs + +The `inputs` field MUST contain any arguments expected by the URI/Ability pair. This MAY be different between different URIs and Abilities, and is thus left to the executor to define the shape of this data. + +### 3.2 DAG-JSON Examples + +Interacting with an HTTP API: + +``` json +{ + "with": "https://example.com/blog/posts", + "do": "crud/create", + "inputs": { + "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 + } + } +} +``` + +Sending Email: + +``` json +{ + "with": "mailto:akiko@example.com", + "do": "msg/send", + "inputs": { + "to": ["boris@example.com", "carol@example.com"], + "subject": "Coffee", + "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" + } +} +``` + +Running WebAssembly from binary: + +``` json +{ + "with": "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==", + "do": "wasm/run", + "inputs": { + "func": "add_one", + "args": [42] + } } +``` + +Executing all of the capabilities in a UCAN directly: -type Scope union { - | All "*" - | {String : Task} +``` json +{ + "with": "ipfs://bafkreiemaanh3kxqchhcdx3yckeb3xvmboztptlgtmnu5jp63bvymxtlva", + "do": "ucan/run", + "inputs": null } +``` + +# 4 Task +A Task is subtype of a [Closure](#3-closure), adding an OPTIONAL metadata field. If not present, the `meta` field defaults to an empty map. A Task can be trivially converted to a Closure by removing the `meta` field. + +``` ipldsch type Task struct { - with URI + with URI do Ability inputs Any - meta {String : Any} -} + meta {String : Any} (implicit {}) +} ``` -## 3.3 JSON Examples +## 4.1 Fields + +### 4.1.1 Closure Fields + +The `with`, `do`, and `inputs` field from [Closure](#3-closure) remain unchanged. + +### 4.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. + +## 4.2 DAG-JSON Examples + +Sending Email: + +``` json +{ + "with": "mailto:akiko@example.com", + "do": "msg/send", + "inputs": { + "to": ["boris@example.com", "carol@example.com"], + "subject": "Coffee", + "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" + }, + "meta": { + "dev/tags": ["friends", "coffee"], + "dev/priority": "high" + } +} +``` -### 3.3.1 Run All +Running WebAssembly from binary: - ``` js +``` json { - "ucan/invoke": { - "v": "0.1.0", - "prf": [ - {"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}, - {"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"} - ], - "run": "*" - "nnc": "abcdef", - "ext": null + "with": "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==", + "do": "wasm/run", + "inputs": { + "func": "add_one", + "args": [42] }, - "sig": {"/": { "bytes:": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA" }} + "meta": { + "dev/notes": "The standard Wasm demo", + "ipvm/verification": "attestation", + "ipvm/resources": { + "gas": 5000 + } + } } ``` -### 3.3.2 Promise Pipelines +# 5 Batch -Promise pipelines are handled in more detail in [§6](#6-promise-pipelining). In brief, they enable taking the output of a task that has not yet run as the input to another task. Here is a simple example: +A Batch is a collection of Tasks, either as a `List` (array) or `Named` (map). In many situations, sending multiple requests in a Batch is more efficient than one-at-a-time. -``` js +A `List` is sugar for a `Named` map, where the keys are the array index number as strings. + +``` ipldsch +type Batch union { + | Named {String : Task} + | List [Task] +} +``` + +## 5.1 Fields + +Each Task in a Batch contains MAY be referenced by a string label. + +## 5.2 DAG-JSON Examples + +### 5.2.1 Named + +``` json { - "ucan/invoke": { - "v": "0.1.0", - "nnc": "02468", - "ext": null, - "prf": [ - {"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}, - {"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"} - ], - "run": { - "notify-bob": { - "with": "mailto://alice@example.com", - "do": "msg/send", - "inputs": [ - { - "to": "bob@example.com", - "subject": "DNSLink for example.com", - "body": "Hello Bob!" - } - ] + "left": { + "with": "https://example.com/blog/posts", + "do": "crud/create", + "inputs": { + "headers": { + "content-type": "application/json" }, - "log-as-done": { - "with": "https://example.com/report" - "do": "crud/update" - "inputs": { - "from": "mailto://alice@exmaple.com", - "to": ["bob@exmaple.com"], - "event": "email-notification", - "value": {"ucan/promise": ["/", "notify-bob"]} // Pipelined promise - } + "payload": { + "title": "How UCAN Tasks Changed My Life", + "body": "This is the story of how one spec changed everything...", + "topics": ["authz", "journal"], + "draft": true } } }, - "sig": {"/": {"bytes:": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7"}} + "right": { + "with": "mailto:akiko@example.com", + "do": "msg/send", + "inputs": { + "to": ["boris@example.com", "carol@example.com"], + "subject": "Coffee", + "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" + }, + "meta": { + "dev/tags": ["friends", "coffee"], + "dev/priority": "high" + } + } } ``` -# 4 Receipt +### 5.2.2 List + +``` json -An invocation receipt is an attestation of the output of an invocation. A receipt MUST be signed by the executor (the `aud` of the associated UCAN). +[ + { + "with": "https://example.com/blog/posts", + "do": "crud/create", + "inputs": { + "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 + } + } + }, + { + "with": "mailto:akiko@example.com", + "do": "msg/send", + "inputs": { + "to": ["boris@example.com", "carol@example.com"], + "subject": "Coffee", + "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" + }, + "meta": { + "dev/tags": ["friends", "coffee"], + "dev/priority": "high" + } + } +] +``` -**NB: a Receipt this does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. +# 6 Invocation -Receipts don't have their own version field. Receipts MUST use the same version as the invocation that they contain. +As [noted in the introduction](#112-lazy-vs-eager-evaluation), there is a difference between a reference to a function and calling that function. [Closures](#3-closure) and [Tasks](#4-task) are not executable until they have been provided provable authority from the [Invoker](#211-invoker) (in the form of UCANs), and signed with the Invoker's private key. -## 4.1 Fields +## 6.1 IPLD Schema + +``` ipldsch +type Invocation struct { + uiv SemVer + run Batch + prf [&UCAN] + nnc String + meta {String : Any} (implicit {}) + sig Varsig +} +``` -| Field | Type | Description | Required | Default | -|--------|----------------------|-----------------------------------------------------------------------|----------|---------| -| `inv` | `&SignedInvocation` | CID of the Invocation that generated this response | Yes | | -| `out` | `{String : Receipt}` | The results of each call, the task's label. MAY contain sub-receipts. | Yes | | -| `meta` | `{String : Any}` | Non-normative extended fields | No | `null` | +## 6.2 Fields -### 4.1.1 Invocation +An Invocation authorizes one or more Tasks to be run. There are a few invariants that MUST hold between the `run`, `prf` and `sig` fields: -The `inv` field MUST include a link to the Invocation that the Receipt is for. +* All of the `run` Tasks MUST be provably authorized by the UCANs in the `prf` field +* All of the `prf` UCANs MUST list the Executor in their `aud` field +* All of the `prf` UCANs MUST list the Invoker in their `iss` +* The `sig` field MUST be produced by the Invoker -### 4.1.2 Output +### 6.2.1 UCAN Task Version -The `out` field MUST contain the output of steps of the call graph, indexed by the task name inside the invocation. If the invocation is the implicit `"*"`, then the base64 hash of the concatenation of the URI, Ability and extensional fields MUST be used. +The `uiv` field MUST contain the SemVer-formatted version of the UCAN Task Specification that this struct conforms to. -The `out` field MAY omit any tasks that have not yet completed, or results which are not public. An `Invocation` may be associated to zero or more `Receipts`. +### 6.2.2 Task -A `Result` MAY include recursive `Receipt` CIDs in on the `Success` branch. As a Task may require subdelegation, the OPTIONAL `rec` field MAY be used to include recursive `Receipt`s. +The `run` field MUST contain a link to the [Task](#31-single-invocation) itself. -### 4.1.3 Metadata Fields +### 6.2.3 Proofs -The 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. +The `prf` field MUST contain links to any UCANs that provide the authority to run the actions. All of the outermost `aud` fields MUST be set to the [Executor](#212-executor)'s DID. All of the outermost `iss` field MUST be set to the [Invoker](#211-invoker)'s DID. + +### 6.2.4 Nonce + +The `nnc` field MUST include a random nonce field expressed in ASCII. This field ensures that multiple invocations are unique. + +### 6.2.5 Metadata + +If present, the OPTIONAL `meta` map MAY contain free form fields. This provides a place for extension of the invocation type. + +Data inside the `meta` field SHOULD NOT be used for [Receipts](#9-receipt). + +### 6.2.6 Signature + +The `sig` field MUST contain a [Varsig](https://github.com/ChainAgnostic/varsig) of the `inv`, `prf`, and `nnc` fields. + +## 6.3 DAG-JSON Examples + +``` json +{ + "uiv": "0.1.0", + "nnc": "6c*97-3=", + "run": { + "left": { + "with": "https://example.com/blog/posts", + "do": "crud/create", + "inputs": { + "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 + } + } + }, + "right": { + "with": "mailto:akiko@example.com", + "do": "msg/send", + "inputs": { + "to": ["boris@example.com", "carol@example.com"], + "subject": "Coffee", + "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" + }, + "meta": { + "dev/tags": ["friends", "coffee"], + "dev/priority": "high" + } + } + }, + "prf": [ + {"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}, + {"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"} + ], + "meta": { + "notes/personal": "I felt like making an invocation today!", + "ipvm/config": { + "time": [5, "minutes"], + "gas": 3000 + } + }, + "sig": {"/": {"bytes:": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7"}} +} +``` + +# 7 Pointer -## 4.2 IPLD Schema +An Invocation Pointer references a specific [Invocation](#6-invocation), either directly by CID (absolute), or from inside the Invocation itself (relative). ``` ipldsch -type SignedReceipt struct { - rec Receipt (rename "ucan/receipt") - sig Varsig +type InvocationPointer union { + | "/" -- Relative to the current invocation + | &TaskInvocation } +``` -type Receipt struct { - inv &SignedInvocation - out {String : Result} - meta optional Any -} +An Invoked Task Pointer references a specific Task inside a Batch, by the name of the label. If the Batch is unlabelled (a `List`), then the index represented as a string MUST be used. -type Result union { - | Success - | Failure +``` ipldsch +type InvokedTaskPointer struct { + envl InvPtr + label String +} representation tuple +``` + +## 7.2 DAG-JSON Examples + +### 7.2.1 Relative + +#### 7.2.1.1 Named + +This relative pointer: + +``` json +["/", "some-label"] +``` + +Will select the marked fields in these Named invocations: + +``` json +{ + "uiv": "0.1.0", + "nnc": "6c*97-3=", + "run": { + "some-label": { // <- Selects this + "with": "https://example.com/blog/posts", + "do": "crud/create", + "inputs": { + "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 + } + } + }, + "some-other-label": { + "with": "mailto:akiko@example.com", + "do": "msg/send", + "inputs": { + "to": ["boris@example.com", "carol@example.com"], + "subject": "Coffee", + "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" + }, + "meta": { + "dev/tags": ["friends", "coffee"], + "dev/priority": "high", + "dev/notes": { + "select-task": ["/", "some-label"] // <- Pointer here + } + } + } + }, + "prf": [{"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"}], + "sig": {"/": {"bytes:": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7"}} } +``` -type Failure struct { - err nullable String - trace Any +``` json +{ + "uiv": "0.1.0", + "nnc": "myNonce529", + "run": { + "some-label": { // <- Selects this + "with": "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==", + "do": "wasm/run", + "inputs": { + "func": "add_one", + "args": [42] + } + } + }, + "meta": { + "dev/notes": { + "select-task": ["/", "some-label"] // <- Pointer here + } + } + "prf": [{"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"}], + "sig": {"/": {"bytes:": "LcZglimIwQ58T0rnkErYshq2S8MMF9G/zRqYXv/PmXs="}} } +``` -type Success struct { - val Any - rec optional &SignedReceipt +### 7.2.1.1 List + +This local pointer: + +``` json +["/", "1"] +``` + +Will select the marked fields in this List invocation: + +``` json +{ + "uiv": "0.1.0", + "nnc": "6c*97-3=", + "run": [ + { + "with": "mailto:akiko@example.com", + "do": "msg/send", + "inputs": { + "to": ["boris@example.com", "carol@example.com"], + "subject": "Coffee", + "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" + }, + "meta": { + "dev/tags": ["friends", "coffee"], + "dev/priority": "high", + "dev/notes": { + "select-task": ["/", "0"] // <- Pointer here + } + } + }, + // Selects this + // vvvvvvvvvvvv + { + "with": "https://example.com/blog/posts", + "do": "crud/create", + "inputs": { + "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 + } + } + } + ], + "prf": [{"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"}], + "sig": {"/": {"bytes:": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7"}} } ``` -## 4.3 JSON Example +### 7.2.2 Absolute + +#### 7.2.2.1 Named + +This absolute pointer: + +``` json +[{"/": "bafkreiff4alf4rdi5mqg4fpxiejgotcnf2zksqanp5ctwzinmqyf7o3i2e"}, "some-label"] +``` + +Will select the marked field in this Named invocation: ``` json +// CID = bafkreiff4alf4rdi5mqg4fpxiejgotcnf2zksqanp5ctwzinmqyf7o3i2e { - "ucan/receipt": { - "v": "0.1.0", - "inv": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], - "out": { - "ok": { - "bafkreiakkqwuffzsxrzseo7fweeicqlnqanhvyffjieh3etsbnnkjxbphi": [ - { - "from": "alice@example.com", - "text": "Hello world!" - }, - { - "from": "bob@example.com", - "text": "What's up?" - } - ], - "bafkreienxymjwglxlb3rkdyeyjt54ddoe4x4qi7a7hyfce3z56zspmy6pm": { - "http": { - "status": 200, - "body": "hello world" - }, - "ms": 476 + "uiv": "0.1.0", + "nnc": "6c*97-3=", + "run": { + "some-label": { // <- Selects this + "with": "https://example.com/blog/posts", + "do": "crud/create", + "inputs": { + "headers": { + "content-type": "application/json" }, - "delegated-task": {"/": "bafkreieiupg4smeb5ammpsydbiea4yvwzwne5ly4hiripy4cjocqiat3ce"} + "payload": { + "title": "How UCAN Tasks Changed My Life", + "body": "This is the story of how one spec changed everything...", + "topics": ["authz", "journal"], + "draft": true + } } + }, + "some-other-label": { + "with": "mailto:akiko@example.com", + "do": "msg/send", + "inputs": { + "to": ["boris@example.com", "carol@example.com"], + "subject": "Coffee", + "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" + }, "meta": { - "notes": "very receipt. such wow.", - "tags": ["dag-house", "fission"] + "dev/tags": ["friends", "coffee"], + "dev/priority": "high", + "dev/notes": { + "select-task": ["/", "some-label"] // <- Pointer here + } } } }, - "sig": {"/": {"bytes": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt_VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA"}} - } + "prf": [{"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"}], + "sig": {"/": {"bytes:": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7"}} +} ``` -# 5 Pointers +#### 7.2.2.1 List -Invocation pointers identify a specific task inside a specific invocation. Since tasks may look identical across calls, they MUST be scoped to a specific [`SignedInvocation`](#32-ipld-schema). +This absolute pointer: -## 5.1 Fields +``` json +[{"/": "bafkreiew2p74l7bq3hnllbduzagdcezlab54ko4lpw72mfcvilh4ov2hkq"}, "1"] +``` + +Will select the marked field in this List invocation: + +``` json +// CID = bafkreiew2p74l7bq3hnllbduzagdcezlab54ko4lpw72mfcvilh4ov2hkq +{ + "uiv": "0.1.0", + "nnc": "6c*97-3=", + "run": [ + { + "with": "mailto:akiko@example.com", + "do": "msg/send", + "inputs": { + "to": ["boris@example.com", "carol@example.com"], + "subject": "Coffee", + "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" + }, + "meta": { + "dev/tags": ["friends", "coffee"], + "dev/priority": "high", + "dev/notes": { + "select-task": ["/", "0"] // <- Pointer here + } + } + }, + // Selects this + // vvvvvvvvvvvv + { + "with": "https://example.com/blog/posts", + "do": "crud/create", + "inputs": { + "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 + } + } + } + ], + "prf": [{"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"}], + "sig": {"/": {"bytes:": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7"}} +} +``` -## 5.2 IPLD Schema +# 8 Result + +A Result records the output of a [Task](#4-task), as well as its success or failure state. + +## 8.1 Variants ``` ipldsch -type TaskPointer struct { - inv InvocationPointer - taskLabel String -} representation tuple +type Result union { + | Any ("ok") -- Success + | Any ("err") -- Failure +} representation keyed +``` -type InvocationPointer union { - | Local "/" - | &SignedInvocation +### 8.1.1 Success + +The `val` field MUST contain the value returned from a successful Task. The exact shape of the returned data is left undefined to allow for flexibility in various Task types. + +``` json +{"ok": 42} +``` + +### 8.1.2 Failure + +The `err` branch MAY contain detail about why execution failed. 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 `Null`. + +``` json +{ + "err": { + "dev/reason": "unauthorized", + "http/status": 401 + } } ``` -## 5.3 JSON Examples +# 9 Receipt + +An Invocation Receipt is an attestation of the Result of an Invocation. A Receipt MUST be signed by the Executor (the `aud` of the associated UCANs). + +**NB: a Receipt this 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. + +## 9.1 Fields + +``` ipldsch +type Receipt struct { + ran &InvokedTaskPointer + out {String : Result} + rec {String : &Receipt} + meta {String : Any} + sig Varsig +} +``` + +### 9.1.1 Task + +The `inv` field MUST include a link to the Task that the Receipt is for. + +### 9.1.2 Output + +The `out` field MUST contain the output of steps of the call graph, indexed by the task name inside the invocation. The `out` field MAY omit any tasks that have not yet completed, or results which are not public. An `Task` may be associated to zero or more `Receipts`. + +A `Result` MAY include recursive `Receipt` CIDs in on the `Success` branch. As a Task may require subdelegation, the OPTIONAL `rec` field MAY be used to include recursive `Receipt`s. + +### 9.1.3 Recursive Receipt + +In the case that an Invocation was subdelegated to another Executor and the Result bubbled up, a recursive Receipt SHOULD be included in the `rec` field. + +### 9.1.4 Metadata Fields + +The 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. + +### 9.1.5 Signature + +The `sig` field MUST contain a [Varsig](https://github.com/ChainAgnostic/varsig) of the `inv`, `out`, and `meta` fields. The signature MUST be generated by the Executor, which means the public key in the `aud` field of the UCANs backing the Task. + +## 9.2 DAG-JSON Examples ``` json -["/", "some-label"] -[{"/": "bafkreiddwsbb7fasjb6k6jodakprtl4lhw6h3g4k7tew7vwwvd63veviie"}, "some-label"] -[{"/": "bafkreiddwsbb7fasjb6k6jodakprtl4lhw6h3g4k7tew7vwwvd63veviie"}, {"/": {"bytes": "bafkreidlqsd6nh6hdgwhr4machsvusobpvn4qfrxfgl5vowoggzk2xpldq"}}] +{ + "inv": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], + "out": { + "status": "ok", + "value": [ + { + "from": "bob@example.com", + "text": "Hello world!" + }, + { + "from": "carol@example.com", + "text": "What's up?" + } + ] + }, + "meta": { + "time": [400, "hours"], + "retries": 2 + }, + "sig": {"/": {"bytes": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt_VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA"}} +} ``` -# 6 Promise Pipelining +# 10 Promise > 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) -At UCAN creation time, the UCAN MAY not yet have all of the information required to construct the next request in a sequence. 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 earlier invocations into the input of a later one. This liberates the invoker from waiting for each step. +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. 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 earlier invocations into the input of a later one. This liberates the invoker from waiting for each step. + +A Promise is a placeholder value MAY be used as a variable placeholder for a concrete value in a [Closure](#3-closure), waiting on a previous step to complete. -The authority to execute later task often cannot be fully attenuated in advance, since the executor controls the reported output of the prior step in a pipeline. When choosing to use pipelining, the invoker MUST delegate capabilities for any of the possible outputs. If tight control is required to limit authority, pipelining SHOULD NOT be used. +One way of seeing the names in a [`Batch`](#5-batch) is as variables for the return of each Closure. These can now be referenced by other Closures. -Promises MUST resolve to a [`Result`](#42-ipld-schema). If a promise resolves to the `Success` branch, the value in the `val` MUST be extracted and substituted for the promise. Behavior is left undefined if the promise returns on the `Failure` branch. +For example, consider the following batch: -Values MUST only be pipelined if they resolve to the `"ok"` branch of the `Result`. In the success case, the value inside the `"ok"` field MUST be extracted and replace the promise. +``` json +{ + "run": { + "create-draft": { + "with": "https://example.com/blog/posts", + "do": "crud/create", + "inputs": { + "payload": { + "title": "How UCAN Tasks Changed My Life", + "body": "This is the story of how one spec changed everything..." + } + } + }, + "get-editors": { + "with": "https://example.com/users/editors", + "do": "crud/read" + }, + "notify": { + "with": "mailto:akiko@example.com", + "do": "msg/send", + "inputs": { + "to": {"ucan/ok": ["/", "right"]}, + "subject": "Coffee", + "body": {"ucan/ok": ["/", "left"]} + } + } + } +} +``` -A promise MAY be placed in any Task field. Substituting into the `with` and `do` fields is NOT RECOMMENDED in fully trustless contexts, as it makes it difficult to understand what is involved in the invocation in advance. +By analogy, this can be interpreted roughly as follows: -## 6.1 Promises - -## 6.1.1 Fields +``` 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, + "subject": "Coffee", + "body": await getEditors +}) +``` -| Field | Type | Description | Required | -|----------------|---------------------|---------------------------------|----------| -| `ucan/promise` | `InvocationPointer` | The Invocation being referenced | Yes | +While a Promise MAY be substituted for any field in a Closure, substituting the `do` field is NOT RECOMMENDED. The `do` field is critical in understanding what kind of action will be performed, and most schedulers will use the -The above table MUST be serialized as a tuple. In JSON, this SHOULD be represented as an array containing the values (but not keys) sequenced in the order they appear in the table. +After resolution, the Closure MUST be validated against the UCANs known to the Executor. A Promise resolved to a Closure that is not backed by a valid UCAN MUST NOT be executed, and SHOULD return an unauthorized error to the user. -## 6.1.2 IPLD Schema +Promises MAY be used inside of a single Invocation's Closures, or across multiple Invocations, and MAY even be across multiple Invokers. As long as the pointer can be resolved, any invoked Task MAY be promised. This is sometimes referred to as ["promise pipelining"](http://erights.org/elib/distrib/pipeline.html). + +A Promise MUST resolve to a [Result](#8-result). If a particular branch's value is required to be unwrapped, the Result tag (`ok` or `err`) MAY be supplied. + +## 10.1 Fields ``` ipldsch -type Promise struct { - promised TaskPointer (rename "ucan/") -} +type Promise union { + | InvokedTaskPointer "ucan/ok" + | InvokedTaskPointer "ucan/err" + | InvokedTaskPointer "ucan/ptr" +} representation keyed ``` -## 6.1.3 JSON Examples +If there are dependencies or ordering required, then you need a promise pipeline - -## 6.2 Pipelining +## 10.2 Pipelines Pipelining uses promises as inputs to determine the required dataflow graph. The following examples both express the following dataflow graph: -``` - ┌────────────────────────────┐ - │ │ - │ dns://example.com?TYPE=TXT │ - │ crud/update │ - │ │ - └───────┬──────────┬─────────┘ - │ │ - │ │ -┌─────────────────────▼───┐ ┌───▼────────────────────────┐ -│ │ │ │ -│ mailto:alice@exaple.com │ │ mailto://alice@example.com │ -│ msg/send │ │ msg/send │ -│ bob@example.com │ │ carol@exmaple.com │ -│ │ │ │ -└─────────────────────┬───┘ └───┬────────────────────────┘ - │ │ - │ │ - ┌────────▼──────────▼────────┐ - │ │ - │ https://example.com/events │ - │ crud/create │ - │ │ - └────────────────────────────┘ -``` - -#### 6.2.1 Batched +### 10.2.1 Batched + +![](./diagrams/batch-pipeline.svg) ``` json { - "ucan/invoke": { - "v": "0.1.0", - "nnc": "abcdef", - "prf": [ - {"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}, - {"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"} - ], - "run": { - "update-dns" : { - "with": "dns://example.com?TYPE=TXT", - "do": "crud/update", - "inputs": { - "value": "hello world", - "content-type": "text/plain; charset=utf-8" - } - }, - "notify-bob": { - "with": "mailto://alice@example.com", - "do": "msg/send", - "inputs": [ - { - "to": "bob@example.com", - "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "update-dns"]} - } - ] - }, - "notify-carol": { - "with": "mailto://alice@example.com", - "do": "msg/send", - "inputs": [ - { - "to": "carol@example.com", - "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["/", "update-dns"]} - } - ] - }, - "log-as-done": { - "with": "https://example.com/report", - "do": "crud/update", - "inputs": [ - { - "from": "mailto://alice@exmaple.com", - "to": ["bob@exmaple.com", "carol@example.com"], - "event": "email-notification", - }, - { - "_": {"ucan/promise": ["/", "notify-bob"]} - }, - { - "_": {"ucan/promise": ["/", "notify-carol"]} - } + "uiv": "0.1.0", + "nnc": "abcdef", + "prf": [ + {"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}, + {"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"} + ], + "run": { + "update-dns" : { + "with": "dns:example.com?TYPE=TXT", + "do": "crud/update", + "inputs": { "value": "hello world"} + }, + "notify-bob": { + "with": "mailto://alice@example.com", + "do": "msg/send", + "inputs": { + "to": "bob@example.com", + "subject": "DNSLink for example.com", + "body": {"ucan/ok": ["/", "update-dns"]} + } + }, + "notify-carol": { + "with": "mailto://alice@example.com", + "do": "msg/send", + "inputs":{ + "to": "carol@example.com", + "subject": "Hey Carol, DNSLink was updated!", + "body": {"ucan/ok": ["/", "update-dns"]} + } + }, + "log-as-done": { + "with": "https://example.com/report", + "do": "crud/update", + "inputs": { + "payload": { + "from": "mailto://alice@exmaple.com", + "to": ["bob@exmaple.com", "carol@example.com"], + "event": "email-notification", + }, + "_": [ + {"ucan/ok": ["/", "notify-bob"]}, + {"ucan/ok": ["/", "notify-carol"]} ] } } @@ -551,126 +1154,73 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The } ``` -### 6.2.2 Serial Pipeline - -``` - ┌────────────────────────────┐ - │ │ - │ dns://example.com?TYPE=TXT │ - │ crud/update │ - │ │ - └───────┬──────────┬─────────┘ - │ │ - │ │ - │ │ - │ ┌───▼────────────────────────┐ - │ │ │ - │ │ mailto://alice@example.com │ - │ │ msg/send │ - │ │ carol@exmaple.com │ - │ │ │ - │ └───┬────────────────────────┘ - │ │ -┌───────────────────────┼──────────┼──────────┐ -│ │ │ │ -│ ┌─────────────────────▼───┐ │ │ -│ │ │ │ │ -│ │ mailto:alice@exaple.com │ │ │ -│ │ msg/send │ │ │ -│ │ bob@example.com │ │ │ -│ │ │ │ │ -│ └─────────────────────┬───┘ │ │ -│ │ │ │ -│ │ │ │ -│ ┌────────▼──────────▼────────┐ │ -│ │ │ │ -│ │ https://example.com/events │ │ -│ │ crud/create │ │ -│ │ │ │ -│ └────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────┘ -``` +### 10.2 Serial Pipeline + +![](./diagrams/serial-pipeline.svg) ``` json { - "ucan/invoke": { - "v": "0.1.0", - "nnc": "abcdef", - "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], - "run": { - "update-dns": { - "with": "dns://example.com?TYPE=TXT", - "do": "crud/update", - "inputs": [ - { - "value": "hello world", - "content-type": "text/plain; charset=utf-8" - } - ] - } + "uiv": "0.1.0", + "nnc": "abcdef", + "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], + "run": { + "update-dns": { + "with": "dns:example.com?TYPE=TXT", + "do": "crud/update", + "inputs": {"value": "hello world"} } }, - "sig": {"/": {"bytes": kQHtTruysx4S8SrvSjTwr6ttTLzc7dd7atANUYT-SRK6v_SX8bjHegWoDak2x6vTAZ6CcVKBt6JGpgnjABpsoL"}} + "sig": {"/": {"bytes": "kQHtTruysx4S8SrvSjTwr6ttTLzc7dd7atANUYT-SRK6v_SX8bjHegWoDak2x6vTAZ6CcVKBt6JGpgnjABpsoL"}} } - +``` + +``` json { - "ucan/invoke": { - "v": "0.1.0", - "nnc": "12345", - "prf": [ - {"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}, - {"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"} - ], - "run": { - "notify-carol": { - "with": "mailto://alice@example.com", - "do": "msg/send", - "inputs": [ - { - "to": "carol@example.com", - "subject": "DNSLink for example.com", - "body": {"ucan/promise": ["bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu", "update-dns"]} - } - ] + "uiv": "0.1.0", + "nnc": "12345", + "prf": [{"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"}], + "run": { + "notify-carol": { + "with": "mailto://alice@example.com", + "do": "msg/send", + "inputs": { + "to": "carol@example.com", + "subject": "Hey Carol, DNSLink was updated!", + "body": {"ucan/ok": [{"/": "bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu"}, "update-dns"]} } } }, "sig": {"/": {"bytes": "XZRSmp5cHaXX6xWzSTxQqC95kQHtTruysx4S8SrvSjTwr6ttTLzc7dd7atANUQJXoWThUiVuCHWdMnQNQJgiJi"}} } +``` +``` json { - "ucan/invoke": { - "v": "0.1.0", - "nnc": "02468", - "ext": null, - "run": { - "notify-bob": { - "with": "mailto://alice@example.com", - "do": "msg/send", - "inputs": [ - { - "to": "bob@example.com", - "subject": "DNSLink for example.com", - "body": {"ucan/promise": [{"/": "bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu"}, "update-dns"]} - } - ] - }, - "log-as-done": { - "with": "https://example.com/report", - "do": "crud/update", - "inputs": [ - { - "from": "mailto://alice@exmaple.com", - "to": ["bob@exmaple.com", "carol@example.com"], - "event": "email-notification", - }, - { - "_": {"ucan/promise": ["/", "notify-bob"]} - }, - { - "_": {"ucan/promise": [{"/": "bafkreidcqdxosqave5u5pml3pyikiglozyscgqikvb6foppobtk3hwkjn4"}, "notify-carol"]} - } + "uiv": "0.1.0", + "nnc": "02468", + "prf": [{"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"}], + "run": { + "notify-bob": { + "with": "mailto://alice@example.com", + "do": "msg/send", + "inputs": { + "to": "bob@example.com", + "subject": "DNSLink for example.com", + "body": {"ucan/ok": [{"/": "bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu"}, "update-dns"]} + } + }, + "log-as-done": { + "with": "https://example.com/report", + "do": "crud/update", + "inputs": { + "payload": { + "from": "mailto://alice@exmaple.com", + "to": ["bob@exmaple.com", "carol@example.com"], + "event": "email-notification" + }, + "_": [ + {"ucan/ok": ["/", "notify-bob"]}, + {"ucan/ok": [{"/": "bafkreidcqdxosqave5u5pml3pyikiglozyscgqikvb6foppobtk3hwkjn4"}, "notify-carol"]} ] } } @@ -679,34 +1229,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The } ``` -# 7 Appendix - -## 7.1 Support Types - -``` ipldsch -type CID String -type URI String -type Ability String -type Path String -type Varsig Bytes - -type SemVer struct { - num NumVer - tag optional String -} representation stringjoin { - join "+" -} - -type NumVer struct { - ma Integer - mi Integer - pa Integer -} representation stringjoin { - join "." -} -``` - -# 8 Prior Art +# 11 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. @@ -718,12 +1241,14 @@ The [Object Capability Network (OCapN)](https://github.com/ocapn/ocapn) protocol [Cap 'n Proto RPC](https://capnproto.org/) is an influential RPC framework [based on concepts from CapTP](https://capnproto.org/rpc.html#specification). -# 9 Acknowledgements +# 12 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 [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. diff --git a/diagrams/batch-pipeline.dot b/diagrams/batch-pipeline.dot new file mode 100644 index 00000000..869c45b3 --- /dev/null +++ b/diagrams/batch-pipeline.dot @@ -0,0 +1,14 @@ +digraph { + splines = ortho + + node [shape = box] + + top -> left -> bottom + top -> right -> bottom + + top [label = "dns:example.com?TYPE=TXT\ncrud/update"] + right [label = "mailto:alice@example.com\nmsg/send\ncarol@example.com"] + + left [label = "mailto:alice@example.com\nmsg/send\nbob@example.com"] + bottom [label = "https://example.com/events\ncrud/create"] +} diff --git a/diagrams/batch-pipeline.svg b/diagrams/batch-pipeline.svg new file mode 100644 index 00000000..b3392f1c --- /dev/null +++ b/diagrams/batch-pipeline.svg @@ -0,0 +1,66 @@ + + + + + + + + + +top + +dns:example.com?TYPE=TXT +crud/update + + + +left + +mailto:alice@example.com +msg/send +bob@example.com + + + +top->left + + + + + +right + +mailto:alice@example.com +msg/send +carol@example.com + + + +top->right + + + + + +bottom + +https://example.com/events +crud/create + + + +left->bottom + + + + + +right->bottom + + + + + diff --git a/diagrams/concepts.dot b/diagrams/concepts.dot index 06c640a5..5a51f69d 100644 --- a/diagrams/concepts.dot +++ b/diagrams/concepts.dot @@ -1,11 +1,19 @@ digraph { - rankdir="LR" + rankdir = "LR" + splines = "line" node [ - style=rounded - shape=box + style = rounded + shape = box ] - Invocation -> Pointer -> Receipt -> Promise -> Pipeline - Invocation -> Batch -> Pipeline + Closure -> Task [label = "+Meta"] + Task -> Batch [label = "Many"] + Task -> Batch + Task -> Batch + Batch -> Invocation [label = "+Auth"] + Invocation -> Result [label = "Output"] + Result -> Promise [label = "Consume"] + Result -> Receipt [label = "+Attest"] + Receipt -> Memoization [label = "Short-circuit"] } diff --git a/diagrams/concepts.svg b/diagrams/concepts.svg index e9eb037a..4aad06a6 100644 --- a/diagrams/concepts.svg +++ b/diagrams/concepts.svg @@ -4,81 +4,118 @@ - + - - + + -Invocation - -Invocation +Closure + +Closure - + -Pointer - -Pointer +Task + +Task - + -Invocation->Pointer - - +Closure->Task + + ++Meta - + Batch - -Batch + +Batch + + + +Task->Batch + + +Many + + + +Task->Batch + + - + + +Task->Batch + + + + + +Invocation + +Invocation + + -Invocation->Batch - - +Batch->Invocation + + ++Auth - - -Receipt - -Receipt + + +Result + +Result - - -Pointer->Receipt - - + + +Invocation->Result + + +Output - + Promise - -Promise + +Promise - - -Receipt->Promise - - + + +Result->Promise + + +Consume - - -Pipeline - -Pipeline + + +Receipt + +Receipt - - -Promise->Pipeline - - + + +Result->Receipt + + ++Attest - - -Batch->Pipeline - - + + +Memoization + +Memoization + + + +Receipt->Memoization + + +Short-circuit diff --git a/diagrams/serial-pipeline.dot b/diagrams/serial-pipeline.dot new file mode 100644 index 00000000..e5add01f --- /dev/null +++ b/diagrams/serial-pipeline.dot @@ -0,0 +1,31 @@ +digraph { + splines = ortho + + node [shape = box] + + top -> left + top -> right + right -> bottom:nne + left -> bottom + + top -> hidden -> left [style = invis] + hidden -> right [style = invis] + hidden [style = invis] + {rank = same; hidden right} + + top [label = "dns:example.com?TYPE=TXT\ncrud/update"] + right [label = "mailto:alice@example.com\nmsg/send\ncarol@example.com"] + + subgraph cluster { + style = dashed + + left [label = "mailto:alice@example.com\nmsg/send\nbob@example.com"] + bottom [label = "https://example.com/events\ncrud/create"] + + hidden2 [style = invis] + left:s -> hidden2:n [style = invis] + hidden2:e -> bottom:w [style = invis] + + {rank = same; hidden2 bottom} + } +} diff --git a/diagrams/serial-pipeline.svg b/diagrams/serial-pipeline.svg new file mode 100644 index 00000000..cb93d651 --- /dev/null +++ b/diagrams/serial-pipeline.svg @@ -0,0 +1,77 @@ + + + + + + + + +cluster + + + + +top + +dns:example.com?TYPE=TXT +crud/update + + + +left + +mailto:alice@example.com +msg/send +bob@example.com + + + +top->left + + + + + +right + +mailto:alice@example.com +msg/send +carol@example.com + + + +top->right + + + + + + + +bottom + +https://example.com/events +crud/create + + + +left->bottom + + + + + + + +right->bottom:nne + + + + + + + + From 26a3aef2710aec7a9ce2634a2643e55c849b0e22 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Dec 2022 00:12:21 -0800 Subject: [PATCH 120/154] Fix examples --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b757d772..8616a786 100644 --- a/README.md +++ b/README.md @@ -922,7 +922,7 @@ type Result union { ### 8.1.1 Success -The `val` field MUST contain the value returned from a successful Task. The exact shape of the returned data is left undefined to allow for flexibility in various Task types. +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 {"ok": 42} @@ -930,7 +930,7 @@ The `val` field MUST contain the value returned from a successful Task. The exac ### 8.1.2 Failure -The `err` branch MAY contain detail about why execution failed. It is left undefined in this specification to allow for Task types to standardize the data that makes sense in their contexts. +The failure branch MAY contain detail about why execution failed wrapped in the `"err"` 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 `Null`. @@ -990,9 +990,8 @@ The `sig` field MUST contain a [Varsig](https://github.com/ChainAgnostic/varsig) ``` json { "inv": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], - "out": { - "status": "ok", - "value": [ + "out": { + "ok": [ { "from": "bob@example.com", "text": "Hello world!" From 32f85627a6125f462619e6120b0e8399aacc293d Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Dec 2022 17:28:36 -0800 Subject: [PATCH 121/154] Typo in title! --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8616a786..011ddecb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# UCAN Task Specification v0.1.0 +# UCAN Invocation Specification v0.1.0 ## Editors From 187d792c08cf4bab5d1624202e6cef401532d693 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 16 Dec 2022 10:13:23 -0800 Subject: [PATCH 122/154] Fix Schema typo per Gozala --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 011ddecb..6a017dce 100644 --- a/README.md +++ b/README.md @@ -220,11 +220,11 @@ type Invocation struct { type InvocationPointer union { | "/" -- Relative to the current invocation - | &TaskInvocation + | &Invocation } type InvokedTaskPointer struct { - envl InvPtr + envl InvocationPointer label String } representation tuple @@ -650,7 +650,7 @@ An Invocation Pointer references a specific [Invocation](#6-invocation), either ``` ipldsch type InvocationPointer union { | "/" -- Relative to the current invocation - | &TaskInvocation + | &Invocation } ``` @@ -658,7 +658,7 @@ An Invoked Task Pointer references a specific Task inside a Batch, by the name o ``` ipldsch type InvokedTaskPointer struct { - envl InvPtr + envl InvocationPointer label String } representation tuple ``` From 20037e30dd6ea9b0e97edced2dae255253a3646b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 16 Dec 2022 21:56:21 +0000 Subject: [PATCH 123/154] Update README.md Co-authored-by: Irakli Gozalishvili Signed-off-by: Brooklyn Zelenka --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a017dce..86a9a859 100644 --- a/README.md +++ b/README.md @@ -989,7 +989,7 @@ The `sig` field MUST contain a [Varsig](https://github.com/ChainAgnostic/varsig) ``` json { - "inv": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], + "ran": {"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}, "out": { "ok": [ { From 53f9d79b6ee6f8d6e2ebd8057249c1cf4308fdeb Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 16 Dec 2022 16:36:24 -0800 Subject: [PATCH 124/154] Uodate ucan/ptr to promise/* (thanks Irakli!) --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 86a9a859..fc5aeb22 100644 --- a/README.md +++ b/README.md @@ -242,9 +242,9 @@ type Result union { } representation keyed type Promise union { - | InvokedTaskPointer "ucan/ok" - | InvokedTaskPointer "ucan/err" - | InvokedTaskPointer "ucan/ptr" + | InvokedTaskPointer "promise/ok" + | InvokedTaskPointer "promise/err" + | InvokedTaskPointer "promise/*" } representation keyed ``` @@ -287,7 +287,7 @@ Later, when we explore [Promises](# 10-promise), this also includes capturing th "with": "mailto://alice@example.com", "do": "msg/send", "inputs": { - "to": {"ucan/promise": ["/", "get-mailing-list"]} + "to": {"promise/*": ["/", "get-mailing-list"]} "subject": "hello", "body": "world" } @@ -1045,9 +1045,9 @@ For example, consider the following batch: "with": "mailto:akiko@example.com", "do": "msg/send", "inputs": { - "to": {"ucan/ok": ["/", "right"]}, + "to": {"promise/ok": ["/", "right"]}, "subject": "Coffee", - "body": {"ucan/ok": ["/", "left"]} + "body": {"promise/ok": ["/", "left"]} } } } @@ -1085,9 +1085,9 @@ A Promise MUST resolve to a [Result](#8-result). If a particular branch's value ``` ipldsch type Promise union { - | InvokedTaskPointer "ucan/ok" - | InvokedTaskPointer "ucan/err" - | InvokedTaskPointer "ucan/ptr" + | InvokedTaskPointer "promise/ok" + | InvokedTaskPointer "promise/err" + | InvokedTaskPointer "promise/*" } representation keyed ``` @@ -1121,7 +1121,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The "inputs": { "to": "bob@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/ok": ["/", "update-dns"]} + "body": {"promise/ok": ["/", "update-dns"]} } }, "notify-carol": { @@ -1130,7 +1130,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The "inputs":{ "to": "carol@example.com", "subject": "Hey Carol, DNSLink was updated!", - "body": {"ucan/ok": ["/", "update-dns"]} + "body": {"promise/ok": ["/", "update-dns"]} } }, "log-as-done": { @@ -1143,8 +1143,8 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The "event": "email-notification", }, "_": [ - {"ucan/ok": ["/", "notify-bob"]}, - {"ucan/ok": ["/", "notify-carol"]} + {"promise/ok": ["/", "notify-bob"]}, + {"promise/ok": ["/", "notify-carol"]} ] } } @@ -1185,7 +1185,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The "inputs": { "to": "carol@example.com", "subject": "Hey Carol, DNSLink was updated!", - "body": {"ucan/ok": [{"/": "bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu"}, "update-dns"]} + "body": {"promise/ok": [{"/": "bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu"}, "update-dns"]} } } }, @@ -1205,7 +1205,7 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The "inputs": { "to": "bob@example.com", "subject": "DNSLink for example.com", - "body": {"ucan/ok": [{"/": "bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu"}, "update-dns"]} + "body": {"promise/ok": [{"/": "bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu"}, "update-dns"]} } }, "log-as-done": { @@ -1218,8 +1218,8 @@ Pipelining uses promises as inputs to determine the required dataflow graph. The "event": "email-notification" }, "_": [ - {"ucan/ok": ["/", "notify-bob"]}, - {"ucan/ok": [{"/": "bafkreidcqdxosqave5u5pml3pyikiglozyscgqikvb6foppobtk3hwkjn4"}, "notify-carol"]} + {"promise/ok": ["/", "notify-bob"]}, + {"promise/ok": [{"/": "bafkreidcqdxosqave5u5pml3pyikiglozyscgqikvb6foppobtk3hwkjn4"}, "notify-carol"]} ] } } From 9f684881c76db5c88bac8bcc1f1838d5f716c46d Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 17 Dec 2022 22:03:46 -0800 Subject: [PATCH 125/154] Fix dangling sentence --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index fc5aeb22..a9f293c6 100644 --- a/README.md +++ b/README.md @@ -561,10 +561,10 @@ type Invocation struct { An Invocation authorizes one or more Tasks to be run. There are a few invariants that MUST hold between the `run`, `prf` and `sig` fields: -* All of the `run` Tasks MUST be provably authorized by the UCANs in the `prf` field -* All of the `prf` UCANs MUST list the Executor in their `aud` field * All of the `prf` UCANs MUST list the Invoker in their `iss` * The `sig` field MUST be produced by the Invoker +* All of the `run` Tasks MUST be provably authorized by the UCANs in the `prf` field +* The Executor(s) MUST be listed in the `aud` field of a UCAN that grants it the authority to perform some action on a resource, or be the root authority for it ### 6.2.1 UCAN Task Version @@ -915,8 +915,8 @@ A Result records the output of a [Task](#4-task), as well as its success or fail ``` ipldsch type Result union { - | Any ("ok") -- Success - | Any ("err") -- Failure + | Any ("ok") -- Success + | {String: Any} ("err") -- Failure } representation keyed ``` @@ -932,7 +932,7 @@ The success branch MUST contain the value returned from a successful Task wrappe The failure branch MAY contain detail about why execution failed wrapped in the `"err"` 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 `Null`. +If no information is available, this field SHOULD be set to `{}`. ``` json { @@ -1045,9 +1045,9 @@ For example, consider the following batch: "with": "mailto:akiko@example.com", "do": "msg/send", "inputs": { - "to": {"promise/ok": ["/", "right"]}, + "to": {"promise/ok": ["/", "get-editors"]}, "subject": "Coffee", - "body": {"promise/ok": ["/", "left"]} + "body": {"promise/ok": ["/", "create-draft"]} } } } @@ -1073,7 +1073,7 @@ const notify = msg.send("mailto:akiko@example.com", { }) ``` -While a Promise MAY be substituted for any field in a Closure, substituting the `do` field is NOT RECOMMENDED. The `do` field is critical in understanding what kind of action will be performed, and most schedulers will use the +While a Promise MAY be substituted for any field in a Closure, substituting the `do` field is NOT RECOMMENDED. The `do` field is critical in understanding what kind of action will be performed, and schedulers SHOULD use this fields to grant atomicity, parallelize tasks, and so on. After resolution, the Closure MUST be validated against the UCANs known to the Executor. A Promise resolved to a Closure that is not backed by a valid UCAN MUST NOT be executed, and SHOULD return an unauthorized error to the user. From 1feb8f8adfc393e60f8165285395419b3b16725b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 17 Dec 2022 22:11:37 -0800 Subject: [PATCH 126/154] Add more text about the promise pointers --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a9f293c6..0051428f 100644 --- a/README.md +++ b/README.md @@ -1075,19 +1075,21 @@ const notify = msg.send("mailto:akiko@example.com", { While a Promise MAY be substituted for any field in a Closure, substituting the `do` field is NOT RECOMMENDED. The `do` field is critical in understanding what kind of action will be performed, and schedulers SHOULD use this fields to grant atomicity, parallelize tasks, and so on. -After resolution, the Closure MUST be validated against the UCANs known to the Executor. A Promise resolved to a Closure that is not backed by a valid UCAN MUST NOT be executed, and SHOULD return an unauthorized error to the user. +After resolution, the Task MUST be validated against the UCANs known to the Executor. A Promise resolved to a Task that is not backed by a valid UCAN MUST NOT be executed, and SHOULD return an unauthorized error to the user. Promises MAY be used inside of a single Invocation's Closures, or across multiple Invocations, and MAY even be across multiple Invokers. As long as the pointer can be resolved, any invoked Task MAY be promised. This is sometimes referred to as ["promise pipelining"](http://erights.org/elib/distrib/pipeline.html). A Promise MUST resolve to a [Result](#8-result). If a particular branch's value is required to be unwrapped, the Result tag (`ok` or `err`) MAY be supplied. -## 10.1 Fields +## 10.1 Enum & Fields + +The following describe a pointer to the eventual value in a Promise, on either branch (`promise/*`), or specifically the success (`promise/ok`) or failure (`promise/err`) branches. ``` ipldsch type Promise union { + | InvokedTaskPointer "promise/*" | InvokedTaskPointer "promise/ok" | InvokedTaskPointer "promise/err" - | InvokedTaskPointer "promise/*" } representation keyed ``` From 9cf808f77725f9a85deeda8ebf4d75cbe9ff0ad8 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 17 Dec 2022 22:44:08 -0800 Subject: [PATCH 127/154] Add words to dictionary --- .github/workflows/words-to-ignore.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 81fa1acf..610fe7bd 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -10,6 +10,7 @@ CapTP DAG-CBOR DAG-JSON ERTP +Enum Gozalishvili Hardt Holmgren @@ -48,6 +49,7 @@ WebAssembly Worthington Zeeshan Zelenka +atomicity auth backend base64url @@ -65,6 +67,7 @@ invariants invoker multiformat outmost +parallelize pipelined pipelining pre-vacation From 1b81bc0f4fdbce093336c594e72cb6d0429b4716 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Mon, 30 Jan 2023 23:59:52 -0800 Subject: [PATCH 128/154] Update README.md Signed-off-by: Irakli Gozalishvili --- README.md | 1818 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 1080 insertions(+), 738 deletions(-) diff --git a/README.md b/README.md index 0051428f..7afc1d7c 100644 --- a/README.md +++ b/README.md @@ -2,23 +2,24 @@ ## Editors -* [Brooklyn Zelenka](https://github.com/expede/), [Fission](https://fission.codes/) +- [Brooklyn Zelenka](https://github.com/expede/), [Fission](https://fission.codes/) +- [Irakli Gozalishvili](https://github.com/Gozala), [DAG House](https://dag.house/) ## Authors -* [Brooklyn Zelenka](https://github.com/expede/), [Fission](https://fission.codes/) -* [Irakli Gozalishvili](https://github.com/Gozala), [DAG House](https://dag.house/) +- [Brooklyn Zelenka](https://github.com/expede/), [Fission](https://fission.codes/) +- [Irakli Gozalishvili](https://github.com/Gozala), [DAG House](https://dag.house/) ## Depends On -* [DAG-CBOR](https://ipld.io/specs/codecs/dag-cbor/spec/) -* [UCAN](https://github.com/ucan-wg/spec/) -* [UCAN-IPLD](https://github.com/ucan-wg/ucan-ipld/) -* [Varsig](https://github.com/ChainAgnostic/varsig/) +- [DAG-CBOR] +- [UCAN] +- [UCAN-IPLD] +- [Varsig] # 0 Abstract -UCAN Invocation defines a format for expressing the intention to run delegated capabilities from a UCAN, 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, the attested receipts from an execution, and how to extend computation via promise pipelining. ## Language @@ -44,11 +45,11 @@ 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](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. 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. -``` js +```js const message = () => alert("hello world") message // Nothing happens message() // A message interups the user @@ -57,16 +58,20 @@ message() // A message interups the user Delegating a capability is like the statement `message`. Task is akin to `message()`. It's true that sometimes we know to run things from their surrounding context without the parentheses: ```js -[1,2,3].map(message) // Message runs 3 times +;[1, 2, 3].map(message) // Message runs 3 times ``` 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 Separation of Concerns +## 1.2 Gossiping of delegations + +In web3.storage user `alice@web.mail` can delegate to store file in her space to `bob@send.io` by sending that delegation to `web3.storage`. If service were to interpret this as invocation it would fail due to principal misalignment. By distinguishing capability invocation from delegation service is able to more correctly handle such a message, if it is an invocation it will still error due to principal misalignment, if it is a delegation it will hold it in Bob's inbox to be picked up when he's comes online. + +## 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](#6-promise-pipelining), 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][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). ``` ────────────────────────────────────────────Time──────────────────────────────────────────────────────► @@ -113,9 +118,9 @@ As we shall see in the [discussion of promise pipelining](#6-promise-pipelining) ## 1.3 A Note On Serialization -The JSON examples below are given in [DAG-JSON](https://ipld.io/docs/codecs/known/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: +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: -``` json +```json // CID {"/": "Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD"} @@ -123,11 +128,32 @@ The JSON examples below are given in [DAG-JSON](https://ipld.io/docs/codecs/know {"/": {"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. +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 Note on Schema Syntax + +We use [IPLD Schema] syntax extended with generics. Standard IPLD Schema can be derived by ignoring parameters enclosed in angle brackets and interpreting parameters as `any`. + +Below schema is in the extended syntax + +```ipldsch +type Box struct { + value T +} +``` + +It compiles down to a following standard syntax + +```ipldsch +type Box struct { + value Box_T +} +type Box_T any +``` -## 1.4 Signatures +## 1.5 Signatures -All payloads described in this spec MUST be signed with a [Varsig](https://github.com/ChainAgnostic/varsig/). +All payloads described in this spec MUST be signed with a [Varsig]. # 2 High-Level Concepts @@ -135,8 +161,8 @@ All payloads described in this spec MUST be signed with a [Varsig](https://githu 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 | Task | +| ---------- | -------------------------------------- | ------------------------------- | | `iss` | Delegator: transfer authority (active) | Invoker: request task (active) | | `aud` | Delegate: gain authority (passive) | Executor: perform task (active) | @@ -152,121 +178,300 @@ 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 Components +## 2.2 Components -![](./diagrams/concepts.svg) +```mermaid +stateDiagram-v2 +direction TB -### 2.2.1 Closure +auth: Authorization -A [Closure](#3-closure) is like a deferred function application: a request to perform some action on a resource with specific inputs. +send: Invocation +send.task: Task +send.promise: Await +read.promise: Await +send.receipt: Receipt -### 2.2.2 Task +read: Invocation +read.task: Task -A [Task](#4-task) extends a Closure with additional metadata that is not used to describe the meaning of the computation or effect to be run. +update: Invocation +update.task: Task -### 2.2.3 Batch +state auth { + direction LR -A [Batch](#5-batch) is a way of requesting more than one action at once. + s-->scope -### 2.2.4 Invocation +} -An [Invocation](#6-invocation) is the cryptographically signed container for a Batch. This is the step that "forces" the "deferred" Closure. +state send.task { + direction LR + + send.task.prf: prf + send.task.do: do + send.task.with: with + send.task.input: input + send.task.nnc: nnc + send.task.meta: meta + + + send.do: msg/send + send.with: mailto꞉//alice@example.com + send.input: {"to"꞉ "bob@example.com", "subject"꞉ "DNSLink for example.com"} + send.prf: UCAN + send.task.meta.data: {tags꞉ ["demo"]} + + send.task.do --> send.do + send.task.with --> send.with + send.task.input --> send.input + send.task.prf --> send.prf + send.task.nnc --> "nonce" + send.task.meta --> send.task.meta.data +} -### 2.2.5 Pointers +state read.task { + direction LR -An [Invocation Pointer](#7-pointer) identifies a specific invocation. An Invoked Task Pointer points to a unique Task inside an Invocation. + read.task.do: do + read.task.with: with + read.task.prf: prf -### 2.2.6 Result -A [Result](#8-result) is the output of a Closure. + read.do: crud/read + read.with: https꞉//example.com/mailinglist + read.prf1: UCAN + read.prf2: UCAN -### 2.2.7 Receipt + read.task.do --> read.do + read.task.with --> read.with + read.task.prf --> read.prf1 + read.task.prf --> read.prf2 +} -A [Receipt](#9-receipt) describes the output of an invocation, referenced by its Invocation Pointer. +state update.task { + direction LR -### 2.2.8 Promise + update.task.do: do + update.task.with: with + update.task.input: input + update.task.prf: prf + update.task.meta: meta -A [promise](#10-promise) is a reference to the receipt of an action that has yet to return a receipt. + update.can: crud/update + update.with: dns꞉example.com?TYPE=TXT + update.input: {value꞉"hello world"} + update.prf: UCAN + update.meta: {dev/tags"꞉ ["friends", "coffee"]} -## 2.3 IPLD Schema + update.task.do --> update.can + update.task.with --> update.with + update.task.input --> update.input + update.task.prf --> update.prf + update.task.meta --> update.meta -``` ipldsch -type Closure struct { - with URI - do Ability - inputs Any } -type Task struct { - with URI - do Ability - inputs Any - meta {String : Any} (implicit {}) +state send { + send.run: task + send.auth: authorization } -type Batch union { - | Named {String : Task} - | List [Task] +state read { + read.run: task + read.auth: authorization } -type Invocation struct { - uiv SemVer - run Batch - prf [&UCAN] - nnc String - meta {String : Any} (implicit {}) - sig Varsig +state update { + update.run: task + update.auth: authorization +} + + +state send.promise { + state send.promise.await <> +} + +state read.promise { + state read.promise.await <> } -type InvocationPointer union { - | "/" -- Relative to the current invocation - | &Invocation +state send.receipt { + send.receipt.sig: sig + send.receipt.ran: ran + send.receipt.out: out + send.receipt.fx: fx + send.receipt.meta: meta + send.receipt.iss: iss + send.receipt.prf: prf + -- + + state send.result <> + send.result --> send.result.ok: ok + send.result --> send.result.error: error + + send.result.ok: success + send.result.error: { error } } -type InvokedTaskPointer struct { - envl InvocationPointer - label String + +send.run --> send.task +send.auth --> auth + +read.run --> read.task +read.auth --> auth + +update.run --> update.task +update.auth --> auth + +scope --> send.task +scope --> read.task +scope --> update.task + + +send.promise.await --> send: ok +send.promise.await --> send: error +send.promise.await --> send: * + +read.promise.await --> read.task: ok +read.promise.await --> read.task: error +read.promise.await --> read.task: * + + +send.receipt.out --> send.result +send.receipt.sig --> send.receipt.ran +send.receipt.sig --> send.receipt.out +send.receipt.sig --> send.receipt.fx +send.receipt.sig --> send.receipt.meta +send.receipt.sig --> send.receipt.iss +send.receipt.sig --> send.receipt.prf + + +send.receipt.ran --> send +``` + +### 2.2.1 Task + +A [Task] is like a deferred function application: a request to perform some action on a resource with specific input. + +### 2.2.2 Authorization + +An [Authorization] is a cryptographically signed proof permitting execution of referenced tasks. It allows invoker to authorize a group of tasks using one cryptographic signature. + +### 2.2.3 Invocation + +An [Invocation] is an invoker authorized instruction to an executor to run the [Task]. + +### 2.2.4 Result + +A [Result] is the output of a [Task]. + +### 2.2.5 Receipt + +A [Receipt] is a cryptographically signed description of the [Invocation] output. + +### 2.2.6 Promise + +A [promise] is a reference to an eventual [Receipt] of an [Invocation]. + +## 2.3 IPLD Schema + +```ipldsch +type Task struct { + v SemVer + + with URI + do Ability + + input In (implicit {}) + meta {String : Any} (implicit {}) + nnc string (implicit "") + + prf [&UCAN] (implicit []) +} + +type SemVer string +type URI string + +type Authorization struct { + # Authorization is denoted by the set of links been authorized + scope [&Any] (implicit []) + # Scope signed by the invoker + s VarSig +} + +type Invocation struct { + task &Task + authorization &Authorization } representation tuple -type Receipt struct { - ran &InvocationPointer - out {String : Result} - rec {String : &Receipt} - meta {String : Any} - sig Varsig -} - -type Result union { - | Any ("ok") -- Success - | Any ("err") -- Failure +type Receipt struct { + ran &Invocation + + # output of the invocation + out Result + # Effects to be performed + fx [&Invocation] (implicit {}) + + # Related receipts + origin optional &Receipt + + # All the other metadata + meta { String: Any } (implicit {}) + + # 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. Should be omitted executor is an issuer. + prf [&UCAN] implicit ([]) + + # Signature from the `iss`. + s Varsig +} + +type Result union { + | Ok ("ok") # Success + | Error ("error") # Error +} representation kinded + +type Promise union { + | &Task ("ucan/task") + | &Invocation ("ucan/invocation") } representation keyed -type Promise union { - | InvokedTaskPointer "promise/ok" - | InvokedTaskPointer "promise/err" - | InvokedTaskPointer "promise/*" +# Promise is a way to reference invocation receipt +type Await union { + &Promise "await/*" + &Promise "await/ok" + &Promise "await/error" } representation keyed ``` -# 3 Closure +# 3 Task -A Closure is the smallest unit of work that can be requested from a UCAN. It describes one `(resource, ability, inputs)` triple. The `inputs` field is free form, and depend on the specific resource and ability being interacted with, and 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 Closure is similar to wrapping a call in an anonymous function: +Using the JavaScript analogy from the introduction, a Task is similar to wrapping a call in an anonymous function: -``` json +```json { + "v": "0.1.0", "with": "mailto://alice@example.com", "do": "msg/send", - "inputs": { + "input": { "to": ["bob@example.com", "carol@example.com"], "subject": "hello", "body": "world" - } + }, + "prf": [ + { "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" } + ] } ``` -``` js +```js // Pseudocode () => msg.send("mailto:alice@example.com", { to: ["bob@example.com", "carol@example.com"], @@ -275,68 +480,127 @@ Using the JavaScript analogy from the introduction, a Closure is similar to wrap }) ``` -Later, when we explore [Promises](# 10-promise), this also includes capturing the promise: +Later, when we explore [Promise]s, this also includes capturing the promise: -``` json +```json { - "mailingList": { + "bafyreihroupaenuxjduzs4ynympv3uawcct4mj4sa7ciuqqlss4httehiu": { + "v": "0.1.0", + "do": "crud/read", "with": "https://example.com/mailinglist", - "do": "crud/read" + "prf": [ + { "/": "bafyreib2dldcev74g5i76bacd4w72gowuraouv2kvnwa2ympvrvtybcsfi" } + ] }, - "sendEmail": { + "bafyreigiz22kfo4bbrsl3jyspm5ykuh2jk5hxmduc5yzijp3dzegvvi6tq": { + "v": "0.1.0", "with": "mailto://alice@example.com", "do": "msg/send", - "inputs": { - "to": {"promise/*": ["/", "get-mailing-list"]} + "input": { + "to": { + "await/*": { + "ucan/task": { + "/": "bafyreihroupaenuxjduzs4ynympv3uawcct4mj4sa7ciuqqlss4httehiu" + } + } + }, "subject": "hello", "body": "world" - } + }, + "prf": [ + { "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" } + ] } } ``` -``` js +```js // Pseudocode -const mailingList = crud.read("https://exmaple.com/mailinglist", {}) // ---┐ - // │ -const sendEmail = () => msg.send("mailto:alice@example.com", { // │ - to: mailingList, // <----------------------------------------------------┘ +const mailingList = crud.read("https://exmaple.com/mailinglist") +msg.send("mailto:alice@example.com", { + to: (await mailingList).ok, subject: "hello", - body: "world" + body: "world", }) ``` - -## 3.1 Fields - -``` ipldsch -type Closure struct { - with URI - do Ability - inputs Any + +## 3.1 Schema + +```ipldsch +type Task struct { + v SemVer + + with URI + do Ability + + input In (implicit {}) + meta {String : Any} (implicit {}) + nnc string (implicit "") + + prf [&UCAN] (implicit []) } + +type SemVer string +type URI string ``` -### 3.1.1 Resource +## 3.2 Fields + +### 3.2.1 UCAN Task Version + +The `v` field MUST contain the SemVer-formatted version of the UCAN Task Specification that this struct conforms to. + +### 3.2.2 Resource The `with` 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.1.2 Ability +### 3.2.3 Ability The `do` 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.1.3 Inputs +### 3.2.4 Input + +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. + +If present, `input` field MUST have an IPLD [map representation][ipld representation], and thus MAY be a: + +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. +3. [map](https://ipld.io/docs/schemas/features/representation-strategies/#map-map-representation) in map representation. + -The `inputs` field MUST contain any arguments expected by the URI/Ability pair. This MAY be different between different URIs and Abilities, and is thus left to the executor to define the shape of this data. +UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type of `input` allowed. -### 3.2 DAG-JSON Examples +If `input` field is not present, it is implicitly a `unit` represented as empty map. -Interacting with an HTTP API: -``` json +### 3.2.5 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. + +### 3.2.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.2.7 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. + + +## 3.3 DAG-JSON Examples + +### 3.3.1 Interacting with an HTTP API + +```json { + "v": "0.1.0", "with": "https://example.com/blog/posts", "do": "crud/create", - "inputs": { + "input": { "headers": { "content-type": "application/json" }, @@ -346,263 +610,142 @@ Interacting with an HTTP API: "topics": ["authz", "journal"], "draft": true } - } + }, + "prf": [ + { "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" } + ] } ``` -Sending Email: +### 3.3.2 Sending Email -``` json -{ +```json +`{ + "v": "0.1.0", "with": "mailto:akiko@example.com", "do": "msg/send", - "inputs": { + "input": { "to": ["boris@example.com", "carol@example.com"], "subject": "Coffee", "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" - } + }, + "meta": { + "dev/priority": "high", + "dev/tags": ["friends", "coffee"] + }, + "prf": [ + { "/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4" } + ] } ``` -Running WebAssembly from binary: +### 3.3.3 Running WebAssembly -``` json +```json { + "v": "0.1.0", "with": "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==", "do": "wasm/run", - "inputs": { + "input": { "func": "add_one", "args": [42] - } -} -``` - -Executing all of the capabilities in a UCAN directly: - -``` json -{ - "with": "ipfs://bafkreiemaanh3kxqchhcdx3yckeb3xvmboztptlgtmnu5jp63bvymxtlva", - "do": "ucan/run", - "inputs": null -} -``` - -# 4 Task - -A Task is subtype of a [Closure](#3-closure), adding an OPTIONAL metadata field. If not present, the `meta` field defaults to an empty map. A Task can be trivially converted to a Closure by removing the `meta` field. - -``` ipldsch -type Task struct { - with URI - do Ability - inputs Any - meta {String : Any} (implicit {}) + }, + "prf": [ + { "/": "bafyreibrqkoin6jzc35hnbrfbsenmcyvd26bn3xyq4of6iwk5z4h63qr34" } + ] } ``` -## 4.1 Fields - -### 4.1.1 Closure Fields +# 4 Authorization -The `with`, `do`, and `inputs` field from [Closure](#3-closure) remain unchanged. -### 4.1.2 Metadata +An [Authorization] is cryptographically signed data set. It represents an authorization to run [Task]s in that are included in `scope` data set. -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. +## 4.1 Schema -## 4.2 DAG-JSON Examples - -Sending Email: - -``` json -{ - "with": "mailto:akiko@example.com", - "do": "msg/send", - "inputs": { - "to": ["boris@example.com", "carol@example.com"], - "subject": "Coffee", - "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" - }, - "meta": { - "dev/tags": ["friends", "coffee"], - "dev/priority": "high" - } +```ipldsch +type Authorization struct { + # Authorization is denoted by the set of links been authorized + scope [&Any] (implicit []) + # Scope signed by the invoker + s VarSig } ``` +### 4.2 Fields -Running WebAssembly from binary: -``` json -{ - "with": "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==", - "do": "wasm/run", - "inputs": { - "func": "add_one", - "args": [42] - }, - "meta": { - "dev/notes": "The standard Wasm demo", - "ipvm/verification": "attestation", - "ipvm/resources": { - "gas": 5000 - } - } -} -``` +#### 4.2.1 Authorization Scope -# 5 Batch +The `scope` field MUST be a set of links been authorized. It SHOULD be encoded as an alphabetically ordered list without duplicates. -A Batch is a collection of Tasks, either as a `List` (array) or `Named` (map). In many situations, sending multiple requests in a Batch is more efficient than one-at-a-time. +If `scope` field is omitted, it is implicitly a an empty list and has no practical use as it authorizes nothing. -A `List` is sugar for a `Named` map, where the keys are the array index number as strings. -``` ipldsch -type Batch union { - | Named {String : Task} - | List [Task] -} -``` +### 4.2.2 Signature -## 5.1 Fields +The `s` field MUST contain a [Varsig] of the [CBOR] encoded `scope` field. -Each Task in a Batch contains MAY be referenced by a string label. +## 4.3 DAG-JSON Example -## 5.2 DAG-JSON Examples -### 5.2.1 Named - -``` json +```json { - "left": { - "with": "https://example.com/blog/posts", - "do": "crud/create", - "inputs": { - "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 - } + "scope": [ + { "/": "bafyreihroupaenuxjduzs4ynympv3uawcct4mj4sa7ciuqqlss4httehiu" }, + { "/": "bafyreigiz22kfo4bbrsl3jyspm5ykuh2jk5hxmduc5yzijp3dzegvvi6tq" } + ], + "s": { + "/": { + "bytes": "7aEDQBzEB/4ViFVHUelItLNcVbXOQEx9rWmfTfoIVnA4QtL08yA3fPcXZd0kRE3Qr0BD61es4R/XHM/yK+kX1PfiWAk" } }, - "right": { - "with": "mailto:akiko@example.com", - "do": "msg/send", - "inputs": { - "to": ["boris@example.com", "carol@example.com"], - "subject": "Coffee", - "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" - }, - "meta": { - "dev/tags": ["friends", "coffee"], - "dev/priority": "high" - } - } } ``` -### 5.2.2 List - -``` json +# 5 Invocation -[ - { - "with": "https://example.com/blog/posts", - "do": "crud/create", - "inputs": { - "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 - } - } - }, - { - "with": "mailto:akiko@example.com", - "do": "msg/send", - "inputs": { - "to": ["boris@example.com", "carol@example.com"], - "subject": "Coffee", - "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" - }, - "meta": { - "dev/tags": ["friends", "coffee"], - "dev/priority": "high" - } - } -] -``` +As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. [Tasks] are not executable until they have been authorized by [Invoker] using [Authorization]. -# 6 Invocation +Invocation describes `(task, authorization)` tuple and instructs [Executor] to run the [Task]. -As [noted in the introduction](#112-lazy-vs-eager-evaluation), there is a difference between a reference to a function and calling that function. [Closures](#3-closure) and [Tasks](#4-task) are not executable until they have been provided provable authority from the [Invoker](#211-invoker) (in the form of UCANs), and signed with the Invoker's private key. +The `authorization` MUST authorize the `task` by including link to it in it's `scope` field. The Invocation of the [Task] that is not included in the linked [Authorization] `scope` MUST be considered invalid. -## 6.1 IPLD Schema +## 5.1 Schema -``` ipldsch +```ipldsch type Invocation struct { - uiv SemVer - run Batch - prf [&UCAN] - nnc String - meta {String : Any} (implicit {}) - sig Varsig -} + task &Task + authorization &Authorization +} representation tuple ``` -## 6.2 Fields - -An Invocation authorizes one or more Tasks to be run. There are a few invariants that MUST hold between the `run`, `prf` and `sig` fields: +## 5.2 Fields -* All of the `prf` UCANs MUST list the Invoker in their `iss` -* The `sig` field MUST be produced by the Invoker -* All of the `run` Tasks MUST be provably authorized by the UCANs in the `prf` field -* The Executor(s) MUST be listed in the `aud` field of a UCAN that grants it the authority to perform some action on a resource, or be the root authority for it +### 5.2.1 -### 6.2.1 UCAN Task Version +The `task` field MUST contain a link to the [Task] to be run. -The `uiv` field MUST contain the SemVer-formatted version of the UCAN Task Specification that this struct conforms to. +### 5.2.2 -### 6.2.2 Task +The `authorization` field MUST contain a link to the [Authorization] that authorizes invoked `task`. -The `run` field MUST contain a link to the [Task](#31-single-invocation) itself. +## 5.3 DAG-JSON Example -### 6.2.3 Proofs +### 5.3.1 Single Invocation -The `prf` field MUST contain links to any UCANs that provide the authority to run the actions. All of the outermost `aud` fields MUST be set to the [Executor](#212-executor)'s DID. All of the outermost `iss` field MUST be set to the [Invoker](#211-invoker)'s DID. -### 6.2.4 Nonce - -The `nnc` field MUST include a random nonce field expressed in ASCII. This field ensures that multiple invocations are unique. - -### 6.2.5 Metadata - -If present, the OPTIONAL `meta` map MAY contain free form fields. This provides a place for extension of the invocation type. - -Data inside the `meta` field SHOULD NOT be used for [Receipts](#9-receipt). - -### 6.2.6 Signature - -The `sig` field MUST contain a [Varsig](https://github.com/ChainAgnostic/varsig) of the `inv`, `prf`, and `nnc` fields. - -## 6.3 DAG-JSON Examples - -``` json +```json { - "uiv": "0.1.0", - "nnc": "6c*97-3=", - "run": { - "left": { + "blocks": { + "bafyreihwj7mouljcwl7s7xpp6sfexptedrmkraoro2tcgiyu4jnjg2rauu": [ + { "/": "bafyreiduxn4l73hs5abjjgpyyazmgar7fd64pdlj62hofr2qm7prnqkwuu" }, + { "/": "bafyreibgo4bq2kxf4ykwz7c4zyvulfytwn46xk5ml2c7tq57fp6d7mhjvq" } + ], + "bafyreiduxn4l73hs5abjjgpyyazmgar7fd64pdlj62hofr2qm7prnqkwuu": { + "v": "0.1.0", "with": "https://example.com/blog/posts", "do": "crud/create", - "inputs": { + "input": { "headers": { "content-type": "application/json" }, @@ -612,80 +755,42 @@ The `sig` field MUST contain a [Varsig](https://github.com/ChainAgnostic/varsig) "topics": ["authz", "journal"], "draft": true } - } - }, - "right": { - "with": "mailto:akiko@example.com", - "do": "msg/send", - "inputs": { - "to": ["boris@example.com", "carol@example.com"], - "subject": "Coffee", - "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" }, - "meta": { - "dev/tags": ["friends", "coffee"], - "dev/priority": "high" + "prf": [ + { "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" } + ] + }, + "bafyreibgo4bq2kxf4ykwz7c4zyvulfytwn46xk5ml2c7tq57fp6d7mhjvq": { + "scope": [ + { "/": "bafyreiduxn4l73hs5abjjgpyyazmgar7fd64pdlj62hofr2qm7prnqkwuu" } + ], + "s": { + "/": { + "bytes": "7aEDQIUWpsYzEkIX8gjunh81kgGFW9KYlEOewghwmowQRCuhkQsxZmpymmfsXVFSL6m79O1s5c+G2pgqODu2qUM4nAY" + } } } }, - "prf": [ - {"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}, - {"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"} - ], - "meta": { - "notes/personal": "I felt like making an invocation today!", - "ipvm/config": { - "time": [5, "minutes"], - "gas": 3000 - } - }, - "sig": {"/": {"bytes:": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7"}} + "roots": [ + { "/": "bafyreihwj7mouljcwl7s7xpp6sfexptedrmkraoro2tcgiyu4jnjg2rauu" } + ] } ``` -# 7 Pointer - -An Invocation Pointer references a specific [Invocation](#6-invocation), either directly by CID (absolute), or from inside the Invocation itself (relative). - -``` ipldsch -type InvocationPointer union { - | "/" -- Relative to the current invocation - | &Invocation -} -``` - -An Invoked Task Pointer references a specific Task inside a Batch, by the name of the label. If the Batch is unlabelled (a `List`), then the index represented as a string MUST be used. - -``` ipldsch -type InvokedTaskPointer struct { - envl InvocationPointer - label String -} representation tuple -``` - -## 7.2 DAG-JSON Examples - -### 7.2.1 Relative - -#### 7.2.1.1 Named - -This relative pointer: - -``` json -["/", "some-label"] -``` - -Will select the marked fields in these Named invocations: +### 5.3.1 Multilpe Invocations -``` json +```json { - "uiv": "0.1.0", - "nnc": "6c*97-3=", - "run": { - "some-label": { // <- Selects this + "blocks": { + "bafyreib527h7rxykieyccwqckfvbrxfwkf6dbiwnilakf7ha3rlofeazdy": [ + { "/": "bafyreiduxn4l73hs5abjjgpyyazmgar7fd64pdlj62hofr2qm7prnqkwuu" }, + { "/": "bafyreigdxhwdln62fekut623s5hipnhkgsc7nurtg4utsrlbt6di4dyptm" } + ], + "bafyreiduxn4l73hs5abjjgpyyazmgar7fd64pdlj62hofr2qm7prnqkwuu": { + "v": "0.1.0", "with": "https://example.com/blog/posts", "do": "crud/create", - "inputs": { + "input": { "headers": { "content-type": "application/json" }, @@ -695,542 +800,757 @@ Will select the marked fields in these Named invocations: "topics": ["authz", "journal"], "draft": true } - } + }, + "prf": [ + { "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" } + ], }, - "some-other-label": { + "bafyreiefdnvc5xuakriluhev5gpqephudvkkwhrbo6ixz5cocvi3camsra": [ + { "/": "bafyreicewv7jbynidkzljwc3okytusrljgjpn7xjamrrcnokgp5qei77gy" }, + { "/": "bafyreigdxhwdln62fekut623s5hipnhkgsc7nurtg4utsrlbt6di4dyptm" } + ], + "bafyreicewv7jbynidkzljwc3okytusrljgjpn7xjamrrcnokgp5qei77gy": { + "v": "0.1.0", "with": "mailto:akiko@example.com", "do": "msg/send", - "inputs": { + "input": { "to": ["boris@example.com", "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 Tasks!", + "subject": "Coffee" }, "meta": { - "dev/tags": ["friends", "coffee"], "dev/priority": "high", - "dev/notes": { - "select-task": ["/", "some-label"] // <- Pointer here + "dev/tags": ["friends", "coffee"] + }, + "prf": [ + { "/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4" } + ], + }, + "bafyreigdxhwdln62fekut623s5hipnhkgsc7nurtg4utsrlbt6di4dyptm": { + "scope": [ + { "/": "bafyreiduxn4l73hs5abjjgpyyazmgar7fd64pdlj62hofr2qm7prnqkwuu" }, + { "/": "bafyreicewv7jbynidkzljwc3okytusrljgjpn7xjamrrcnokgp5qei77gy" } + ] + "s": { + "/": { + "bytes": "7aEDQIU6IPBZ8JMaB7mmTAwmIpMR4qMGUdD8R/cU8rc5uzxSQhetKw1dFdePqbffdC6aKdifv5MXO0tA2iKfN7tkhwI" } - } + }, } }, - "prf": [{"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"}], - "sig": {"/": {"bytes:": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7"}} + "roots": [ + { "/": "bafyreib527h7rxykieyccwqckfvbrxfwkf6dbiwnilakf7ha3rlofeazdy" }, + { "/": "bafyreiefdnvc5xuakriluhev5gpqephudvkkwhrbo6ixz5cocvi3camsra" } + ] } ``` -``` json -{ - "uiv": "0.1.0", - "nnc": "myNonce529", - "run": { - "some-label": { // <- Selects this - "with": "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==", - "do": "wasm/run", - "inputs": { - "func": "add_one", - "args": [42] - } - } - }, - "meta": { - "dev/notes": { - "select-task": ["/", "some-label"] // <- Pointer here - } - } - "prf": [{"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"}], - "sig": {"/": {"bytes:": "LcZglimIwQ58T0rnkErYshq2S8MMF9G/zRqYXv/PmXs="}} +# 6 Result + +A `Result` records the output of the [Task], as well as its success or failure state. + +## 6.1 Schema + +```ipldsch +type Result union { + | Ok ("ok") # Success + | Error ("error") # Error } ``` -### 7.2.1.1 List +## 6.2 Variants -This local pointer: +## 6.2.1 Success -``` json -["/", "1"] +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 +{"ok": 42} ``` -Will select the marked fields in this List invocation: +## 6.2.2 Failure -``` json +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 { - "uiv": "0.1.0", - "nnc": "6c*97-3=", - "run": [ - { - "with": "mailto:akiko@example.com", - "do": "msg/send", - "inputs": { - "to": ["boris@example.com", "carol@example.com"], - "subject": "Coffee", - "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" - }, - "meta": { - "dev/tags": ["friends", "coffee"], - "dev/priority": "high", - "dev/notes": { - "select-task": ["/", "0"] // <- Pointer here - } - } - }, - // Selects this - // vvvvvvvvvvvv - { - "with": "https://example.com/blog/posts", - "do": "crud/create", - "inputs": { - "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 - } - } - } - ], - "prf": [{"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"}], - "sig": {"/": {"bytes:": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7"}} + "error": { + "dev/reason": "unauthorized", + "http/status": 401 + } } ``` -### 7.2.2 Absolute +# 7 Receipt -#### 7.2.2.1 Named +An [Invocation] Receipt is an attestation of the [Result] of an Invocation. A Receipt MUST be signed by the [Executor] or it's delegate. If signed by delegate, proof of delegation from [Executor] to the Issuer (the `iss` of the receipt) MUST be provided in `prf`. -This absolute pointer: +**NB: a Receipt this does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. -``` json -[{"/": "bafkreiff4alf4rdi5mqg4fpxiejgotcnf2zksqanp5ctwzinmqyf7o3i2e"}, "some-label"] -``` +Receipts MUST use the same version as the invocation that they contain. -Will select the marked field in this Named invocation: +## 7.1 Schema -``` json -// CID = bafkreiff4alf4rdi5mqg4fpxiejgotcnf2zksqanp5ctwzinmqyf7o3i2e -{ - "uiv": "0.1.0", - "nnc": "6c*97-3=", - "run": { - "some-label": { // <- Selects this - "with": "https://example.com/blog/posts", - "do": "crud/create", - "inputs": { - "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 - } - } - }, - "some-other-label": { - "with": "mailto:akiko@example.com", - "do": "msg/send", - "inputs": { - "to": ["boris@example.com", "carol@example.com"], - "subject": "Coffee", - "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" - }, - "meta": { - "dev/tags": ["friends", "coffee"], - "dev/priority": "high", - "dev/notes": { - "select-task": ["/", "some-label"] // <- Pointer here - } - } - } - }, - "prf": [{"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"}], - "sig": {"/": {"bytes:": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7"}} -} -``` +```ipldsch +type Receipt struct { + ran &Invocation -#### 7.2.2.1 List + # output of the invocation + out Result + # Effects to be performed + fx [&Invocation] (implicit {}) -This absolute pointer: + # Related receipts + origin optional &Receipt -``` json -[{"/": "bafkreiew2p74l7bq3hnllbduzagdcezlab54ko4lpw72mfcvilh4ov2hkq"}, "1"] -``` + # All the other metadata + meta { String: Any } (implicit {}) -Will select the marked field in this List invocation: + # Principal that issued this receipt. If omitted issuer is + # inferred from the invocation task audience. + iss optional Principal -``` json -// CID = bafkreiew2p74l7bq3hnllbduzagdcezlab54ko4lpw72mfcvilh4ov2hkq -{ - "uiv": "0.1.0", - "nnc": "6c*97-3=", - "run": [ - { - "with": "mailto:akiko@example.com", - "do": "msg/send", - "inputs": { - "to": ["boris@example.com", "carol@example.com"], - "subject": "Coffee", - "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!" - }, - "meta": { - "dev/tags": ["friends", "coffee"], - "dev/priority": "high", - "dev/notes": { - "select-task": ["/", "0"] // <- Pointer here - } - } - }, - // Selects this - // vvvvvvvvvvvv - { - "with": "https://example.com/blog/posts", - "do": "crud/create", - "inputs": { - "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 - } - } - } - ], - "prf": [{"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"}], - "sig": {"/": {"bytes:": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7"}} + # When issuer is different from executor this MUST hold a UCAN + # delegation chain from executor to the issuer. Should be omitted executor is an issuer. + prf [&UCAN] implicit ([]) + + # Signature from the `iss`. + s Varsig } ``` -# 8 Result +## 7.2 Fields -A Result records the output of a [Task](#4-task), as well as its success or failure state. +### 7.2.1 Ran Invocation -## 8.1 Variants +The `ran` field MUST include a link to the [Invocation] that the Receipt is for. -``` ipldsch -type Result union { - | Any ("ok") -- Success - | {String: Any} ("err") -- Failure -} representation keyed -``` +### 7.2.2 Output -### 8.1.1 Success +The `out` field MUST contain the output of the invocation in [Result] format. -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. +### 7.2.3 Effects -``` json -{"ok": 42} -``` +Some [Task]s may describe complex workflows with multiple, sometimes concurrent, steps. Such [Task]s MAY capture each state update using `out` of the chained [Receipt] and consequent (concurrent) steps using `fx` [Invocation]s. -### 8.1.2 Failure +The OPTIONAL `fx` field, if present MUST contain set of concurrent [Invocation]s that need to be run by the [Executor] to complete execution of the ongoing [Invocation]. [Executor] MUST run contained [Invocation]s concurrently unless they are ordered using promise pipelining. -The failure branch MAY contain detail about why execution failed wrapped in the `"err"` tag. It is left undefined in this specification to allow for Task types to standardize the data that makes sense in their contexts. +If `fx` field is omitted, or if field is set to an empty list it denote an end of the execution. -If no information is available, this field SHOULD be set to `{}`. +### 7.2.4 Linked Receipts -``` json -{ - "err": { - "dev/reason": "unauthorized", - "http/status": 401 - } -} -``` +When [Invocation] consisets of multiple steps, each step MAY produce a receipt, each subsequent one SHOULD be chained with previous receipt using `origin` field. -# 9 Receipt +### 6.2.4 Metadata Fields -An Invocation Receipt is an attestation of the Result of an Invocation. A Receipt MUST be signed by the Executor (the `aud` of the associated UCANs). +The 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. -**NB: a Receipt this does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. +### 6.2.5 Receipt Issuer -Receipts MUST use the same version as the invocation that they contain. +The OPTIONAL `iss` field, if present MUST contain the signer of the receipt. It MUST be an [Executor] or it's delegate. If delegate proof of delegation MUST be provided in `prf` field. -## 9.1 Fields +If `iss` field is omitted, it MUST implicitly imply an [Executor]. -``` ipldsch -type Receipt struct { - ran &InvokedTaskPointer - out {String : Result} - rec {String : &Receipt} - meta {String : Any} - sig Varsig -} -``` +### 6.2.6 Proofs -### 9.1.1 Task -The `inv` field MUST include a link to the Task that the Receipt is for. +The `prf` field MUST contain links to UCAN(s) that that delegate authority to perform the invocation from the [Executor] to the Receipt issuer (`iss`). If [Executor] and the Issuer are same no proofs are required. -### 9.1.2 Output +### 6.2.7 Signature -The `out` field MUST contain the output of steps of the call graph, indexed by the task name inside the invocation. The `out` field MAY omit any tasks that have not yet completed, or results which are not public. An `Task` may be associated to zero or more `Receipts`. +The `s` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without `s` field. The signature MUST be generated by the issuer (`iss`). -A `Result` MAY include recursive `Receipt` CIDs in on the `Success` branch. As a Task may require subdelegation, the OPTIONAL `rec` field MAY be used to include recursive `Receipt`s. +## 6.3 DAG-JSON Examples -### 9.1.3 Recursive Receipt +### 6.3.1 Issued by Executor -In the case that an Invocation was subdelegated to another Executor and the Result bubbled up, a recursive Receipt SHOULD be included in the `rec` field. +```json +{ + "ran": { + "/": "bafyreibt3t5uzjiwn4b3rszto6ox2z4najv45rzi75dooti3ad2rl7qkoi" + }, + "out": { + "ok": { + "from": "bob@example.com", + "text": "Hello world!" + } + }, + "meta": { + "retries": 2, + "time": [400, "hours"] + }, + "s": { + "/": { + "bytes": "7aEDQJBRBQPMyopsMw84vBQa2EdrRZtz9Kk7oF+tZ5eVIKSjeQwy1ReCGVXkt56cEhlOER7uOpIugSwUtf8VapW1TQM" + } + } +} +``` -### 9.1.4 Metadata Fields +### 6.3.2 Issued by Delegate -The 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. +```json +{ + "ran": { + "/": "bafyreicewv7jbynidkzljwc3okytusrljgjpn7xjamrrcnokgp5qei77gy" + }, + "iss": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", + "out": { + "ok": { + "from": "bob@example.com", + "text": "Hello world!" + } + }, + "meta": { + "retries": 2, + "time": [400, "hours"] + }, + "prf": [ + { "/": "bafyreihfgvlol74ugosa5gkzvbsghmq7wiqn4xvgack4uwn4qagrml6p74" } + ], + "s": { + "/": { + "bytes": "7aEDQBAv3B11GOo5Yoa/wMILlK0L3565ainer90OFa0h8XA4awZFKB3bfq0DL00HH+r4tKVq3k440HIG0apYcWppFwI" + } + } +} +``` -### 9.1.5 Signature +### 6.3.3 Receipts with effects -The `sig` field MUST contain a [Varsig](https://github.com/ChainAgnostic/varsig) of the `inv`, `out`, and `meta` fields. The signature MUST be generated by the Executor, which means the public key in the `aud` field of the UCANs backing the Task. +```json +{ + "ran": { + "/": "bafyreicrhbr7jjt6pvhldnrl7g6tr4r5uspgea52xo23a2yegfnpspeste" + }, + "out": { + "ok": {} + }, + "fx": [ + { + "/": "bafyreib6mgm5few6pnajc25h6r6trp2kbi6xrmhafnmby3hrflmgql2kna" + } + ], + "s": { + "/": { + "bytes": "7aEDQMO5k6OtYgHSRVIR2sqn97TTfrhLrdTSusoYOAaCV2lg37xI22QMjMhW44eVgkIRcWcbLO4YdrJfcwG4t3jzegM" + } + } +} +``` -## 9.2 DAG-JSON Examples +### 6.3.4 Chained Receipt -``` json +```json { - "ran": {"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}, - "out": { - "ok": [ - { - "from": "bob@example.com", - "text": "Hello world!" - }, - { - "from": "carol@example.com", - "text": "What's up?" - } - ] + "ran": { + "/": "bafyreicrhbr7jjt6pvhldnrl7g6tr4r5uspgea52xo23a2yegfnpspeste" }, - "meta": { - "time": [400, "hours"], - "retries": 2 + "out": { + "ok": { + "capacity": "3GiB" + } + }, + "origin": { + "/": "bafyreiga5grjokrjgjn62kbwdf6oz3w5m4hj4ck3ln6k6bw3wguiv2s6oy" }, - "sig": {"/": {"bytes": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt_VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA"}} + "s": { + "/": { + "bytes": "7aEDQFhBCM0NkHNMpHVn1UWnI90S0hHKMiDbqj4Gf3U3QNRpVM5/Ta9Uy94khq14TREmEWBwkqVMEk/EJ46a4NF33AY" + } + } } ``` -# 10 Promise +# 8 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](https://github.com/erights), [Robust Composition](http://www.erights.org/talks/thesis/markm-thesis.pdf) -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. 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 earlier invocations into the input of a later one. This liberates the invoker from waiting for each step. +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. -A Promise is a placeholder value MAY be used as a variable placeholder for a concrete value in a [Closure](#3-closure), waiting on a previous step to complete. +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. -One way of seeing the names in a [`Batch`](#5-batch) is as variables for the return of each Closure. These can now be referenced by other Closures. +A [Promise] / [Await] MAY be used as a variable placeholder for a concrete value in an [Invocation] output, waiting on a previous step to complete. -For example, consider the following batch: +For example, consider the following invocation batch: -``` json +```json { - "run": { - "create-draft": { - "with": "https://example.com/blog/posts", - "do": "crud/create", - "inputs": { - "payload": { - "title": "How UCAN Tasks Changed My Life", - "body": "This is the story of how one spec changed everything..." - } + "bafyreif7wiz4mz3ojmmvrz2p5m6ay5nqbi7ghwmheccv5rqgjtkrk5wyi4": { + "v": "0.1.0", + "with": "https://example.com/blog/posts", + "do": "crud/create", + "input": { + "payload": { + "body": "This is the story of how one spec changed everything...", + "title": "How UCAN Tasks Changed My Life" } }, - "get-editors": { - "with": "https://example.com/users/editors", - "do": "crud/read" - }, - "notify": { - "with": "mailto:akiko@example.com", - "do": "msg/send", - "inputs": { - "to": {"promise/ok": ["/", "get-editors"]}, - "subject": "Coffee", - "body": {"promise/ok": ["/", "create-draft"]} + "prf": [ + { "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" } + ] + }, + "bafyreifc2ytuc5dv454a7c6cxk3mm7gt6skoptzj5rtc3ijf65v2bjxssy": { + "v": "0.1.0", + "with": "https://example.com/users/editors", + "do": "crud/read", + "prf": [ + { "/": "bafyreie3ukg4h2kf7lnx7k62kjujlo2a5l66rh7e7vlj52fnsrj7tuc2ya" } + ] + }, + "bafyreibodwqz5ghsvf6nmopdbkksp46roc6zakmmd7gv6flymc2edeeq3q": { + "v": "0.1.0", + "with": "mailto:akiko@example.com", + "do": "msg/send", + "input": { + "to": { + "await/ok": { + "ucan/task": { + "/": "bafyreifc2ytuc5dv454a7c6cxk3mm7gt6skoptzj5rtc3ijf65v2bjxssy" + } + } + }, + "subject": "Coffee", + "body": { + "await/ok": { + "ucan/task": { + "/": "bafyreif7wiz4mz3ojmmvrz2p5m6ay5nqbi7ghwmheccv5rqgjtkrk5wyi4" + } + } } - } + }, + "prf": [ + { "/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4" } + ] } } ``` -By analogy, this can be interpreted roughly as follows: +By analogy, above examples can be interpreted roughly as follows: -``` js +```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..." - } + 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, - "subject": "Coffee", - "body": await getEditors + to: (await createDraft).ok, + subject: "Coffee", + body: (await getEditors).ok, }) ``` -While a Promise MAY be substituted for any field in a Closure, substituting the `do` field is NOT RECOMMENDED. The `do` field is critical in understanding what kind of action will be performed, and schedulers SHOULD use this fields to grant atomicity, parallelize tasks, and so on. +While a [Await] MAY be substituted for any field in a [Task], substituting the `do` field is NOT RECOMMENDED. The `do` field is critical in understanding what kind of action will be performed, and schedulers SHOULD use this fields to grant atomicity, parallelize tasks, and so on. + +After resolution, the [Invocation] MUST be validated against the [Authorization] and linked UCAN proofs by the [Executor]. A Promise resolved to an [Invocation] that is not backed by a valid UCAN MUST NOT be executed, and SHOULD return an unauthorized error to the user. A Promise resolved to an [Invocation] with the [Authorization] that does not include invoked [Task] MUST NOT be executed, and SHOULD return an unauthorized error to the user. + +Promises 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). + + +## 8.1 Promise + +A `Promise` describes eventual output `Result` of the [Invocation]. + +### 8.1.1 Schema + +```ipldsch +type Promise union { + | &Invocation ("ucan/invocation") + | &Task ("ucan/task") +} representation keyed +``` + +#### 8.1.2 Variants -After resolution, the Task MUST be validated against the UCANs known to the Executor. A Promise resolved to a Task that is not backed by a valid UCAN MUST NOT be executed, and SHOULD return an unauthorized error to the user. +##### 8.1.2.1 Invocation Promise -Promises MAY be used inside of a single Invocation's Closures, or across multiple Invocations, and MAY even be across multiple Invokers. As long as the pointer can be resolved, any invoked Task MAY be promised. This is sometimes referred to as ["promise pipelining"](http://erights.org/elib/distrib/pipeline.html). +A `Promise` of the [Invocation] MUST be a link to that invocation wrapped in the `"ucan/invocation"` tag. -A Promise MUST resolve to a [Result](#8-result). If a particular branch's value is required to be unwrapped, the Result tag (`ok` or `err`) MAY be supplied. +```ts +{ + "ucan/invocation": { + "/": "bafyreihdw5dpnggixaee3rwn5kr3vhvfvokj3hi24uk2swp4nizyjdhdeq" + } +} +``` + +##### 8.1.2.2 Task Promise + +An `Promise` of the [Invocation] that shares [Authorization] with a [Task] referecing it MUST be a link to the invocation [Task] wrapped in the `"ucan/task"` tag. -## 10.1 Enum & Fields +Note that [Task]s with the same [Authorization] will be unable to referece [Invocation] since [Authorization] CAN be created after all the [Task]s being authorized. The `"ucan/task"` tagged Promise MAY be used in such cases. -The following describe a pointer to the eventual value in a Promise, on either branch (`promise/*`), or specifically the success (`promise/ok`) or failure (`promise/err`) branches. +## 8.2 Await -``` ipldsch -type Promise union { - | InvokedTaskPointer "promise/*" - | InvokedTaskPointer "promise/ok" - | InvokedTaskPointer "promise/err" +An `Await` describes an output of the [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. + +### 8.2.1 Schema + +```ipldsch +type Await union { + &Promise "await/*" + &Promise "await/ok" + &Promise "await/error" } representation keyed ``` -If there are dependencies or ordering required, then you need a promise pipeline +#### 8.2.2 Variants + +##### 8.2.2.1 Success -## 10.2 Pipelines +The successful output of the [Invocation] MAY be referenced by wrapping a [Promise] in the `"await/ok"` tag. -Pipelining uses promises as inputs to determine the required dataflow graph. The following examples both express the following dataflow graph: +[Executor] MUST fail [Invocation] that `Await`s succeful output of the failed [Invocation]. -### 10.2.1 Batched +[Executor] MUST substitute [Task] field set to the [Await] of the succesful [Invocation] with an (unwrapped) `ok` value of the output. -![](./diagrams/batch-pipeline.svg) +##### 8.2.2.1 Failure - ``` json +The failed output of the [Invocation] MAY be renfereced by wrapping a [Promise] in the `"await/error"` tag. + +[Executor] MUST fail [Invocation] that `Await`s failed output of the succesful [Invocation]. + +[Executor] MUST substitute [Task] field set to the [Await] of the failed [Invocation] with an (unwrapped) `error` value of the output. + +##### 8.2.2.1 Result + +The [Result] output of the [Invocation] MAY be reference by wrapping a [Promise] in the `"await/*"` tag. + +[Executor] MUST substitute [Task] field set to the [Await] of the [Invocation] with a `Result` value of the output. + + +## 8.3 Dataflows + +Pipelining uses [Await] as inputs to determine the required dataflow graph. The following examples both express the following dataflow graph: + +### 7.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 { - "uiv": "0.1.0", - "nnc": "abcdef", - "prf": [ - {"/": "bafkreie2cyfsaqv5jjy2gadr7mmupmearkvcg7llybfdd7b6fvzzmhazuy"}, - {"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"} - ], - "run": { - "update-dns" : { + "blocks": { + "bafyreiesse5saoa5dj7f5mh7sffy57vfhnjm6tpgwmwxe3ncwin2hwqsoy": [ + { "/": "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli" }, + { "/": "bafyreier7y2snffy7x75y4ri6ahiflxmz7ksklizzwpphwgpfotc3omo2y" } + ], + "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli": { + "v": "0.1.0", "with": "dns:example.com?TYPE=TXT", "do": "crud/update", - "inputs": { "value": "hello world"} + "input": { + "value": "hello world" + }, + "prf": [ + { "/": "bafyreieynwqrabzdhgl652ftsk4mlphcj3bxchkj2aw5eb6dc2wxieilau" } + ], }, - "notify-bob": { + "bafyreieeipburgtzg4hr2ghxgspc53szrkstaz4syjat3tex75hjdrau3y": [ + { "/": "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm" }, + { "/": "bafyreier7y2snffy7x75y4ri6ahiflxmz7ksklizzwpphwgpfotc3omo2y" } + ], + "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm": { + "v": "0.1.0", "with": "mailto://alice@example.com", "do": "msg/send", - "inputs": { - "to": "bob@example.com", + "input": { "subject": "DNSLink for example.com", - "body": {"promise/ok": ["/", "update-dns"]} - } + "to": "bob@example.com", + "body": { + "await/ok": { + "ucan/task": { + "/": "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli" + } + } + } + }, + "prf": [ + { "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" } + ] }, - "notify-carol": { + "bafyreif7cskac7ongewanqahk2vljkk5rsiblm2vqqo57j2d6mgeuhtuxq": [ + { "/": "bafyreifjjvdd6hoi7wjot7tjaubwztqldds332bgq6a4n37d6s5slxkbvy" }, + { "/": "bafyreier7y2snffy7x75y4ri6ahiflxmz7ksklizzwpphwgpfotc3omo2y" } + ], + "bafyreifjjvdd6hoi7wjot7tjaubwztqldds332bgq6a4n37d6s5slxkbvy": { + "v": "0.1.0", "with": "mailto://alice@example.com", "do": "msg/send", - "inputs":{ + "input": { "to": "carol@example.com", "subject": "Hey Carol, DNSLink was updated!", - "body": {"promise/ok": ["/", "update-dns"]} - } + "body": { + "await/ok": { + "ucan/task": { + "/": "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli" + } + } + } + }, + "prf": [ + { "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" } + ] }, - "log-as-done": { + "bafyreid5bb7z7l4ti57gdep6tbsnam47555ivnh3znpylo2n7qqyiiggqm": [ + { "/": "bafyreidxauqdrexpqw66zlo3q6cmr2vuuvb3vletnugkf33uuxwt2um4ry" }, + { "/": "bafyreier7y2snffy7x75y4ri6ahiflxmz7ksklizzwpphwgpfotc3omo2y" } + ], + "bafyreidxauqdrexpqw66zlo3q6cmr2vuuvb3vletnugkf33uuxwt2um4ry": { + "v": "0.1.0", "with": "https://example.com/report", "do": "crud/update", - "inputs": { + "input": { "payload": { - "from": "mailto://alice@exmaple.com", - "to": ["bob@exmaple.com", "carol@example.com"], "event": "email-notification", + "from": "mailto://alice@exmaple.com", + "to": [ + "bob@exmaple.com", + "carol@example.com" + ] }, "_": [ - {"promise/ok": ["/", "notify-bob"]}, - {"promise/ok": ["/", "notify-carol"]} - ] + { + "await/ok": { + "ucan/task": { + "/": "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm" + } + } + }, + { + "await/ok": { + "ucan/task": { + "/": "bafyreifjjvdd6hoi7wjot7tjaubwztqldds332bgq6a4n37d6s5slxkbvy" + } + } + } + ], + }, + "prf": [ + { "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" } + ] + }, + "bafyreier7y2snffy7x75y4ri6ahiflxmz7ksklizzwpphwgpfotc3omo2y": { + "scope": [ + { "/": "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli" }, + { "/": "bafyreifjjvdd6hoi7wjot7tjaubwztqldds332bgq6a4n37d6s5slxkbvy" }, + { "/": "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm" }, + { "/": "bafyreidxauqdrexpqw66zlo3q6cmr2vuuvb3vletnugkf33uuxwt2um4ry" } + ], + "s": { + "/": { + "bytes": "7aEDQPdMfJiaNTzmTVHiJhkcX6mlKOG2piEk0OtpLslJeaimx4uM4/hGcadQ3Z6qhu2j761PW4RKyC1+BiWB+jO7LwA" + } } } }, - "sig": {"/": {"bytes": "bdNVZn_uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT-SRK6v_SX8bjt-VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA"}} + "roots": [ + { "/": "bafyreiesse5saoa5dj7f5mh7sffy57vfhnjm6tpgwmwxe3ncwin2hwqsoy" }, + { "/": "bafyreif7cskac7ongewanqahk2vljkk5rsiblm2vqqo57j2d6mgeuhtuxq" }, + { "/": "bafyreieeipburgtzg4hr2ghxgspc53szrkstaz4syjat3tex75hjdrau3y" }, + { "/": "bafyreid5bb7z7l4ti57gdep6tbsnam47555ivnh3znpylo2n7qqyiiggqm" } + ] } ``` -### 10.2 Serial Pipeline -![](./diagrams/serial-pipeline.svg) +### 7.3.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 +```json { - "uiv": "0.1.0", - "nnc": "abcdef", - "prf": [{"/": "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e"}], - "run": { - "update-dns": { + "blocks": { + "bafyreid5ltmhwuhgcbxa3dwd5erymqdpfsyhlwudlfylu3wrzy6fjbvuoy": [ + { "/": "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli" }, + { "/": "bafyreicwrzsyonu4efnwtmwqax2s2fbv5padbyk66zwl6kkvrkrd5qavpy" } + ], + "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli": { + "v": "0.1.0", "with": "dns:example.com?TYPE=TXT", "do": "crud/update", - "inputs": {"value": "hello world"} - } - }, - "sig": {"/": {"bytes": "kQHtTruysx4S8SrvSjTwr6ttTLzc7dd7atANUYT-SRK6v_SX8bjHegWoDak2x6vTAZ6CcVKBt6JGpgnjABpsoL"}} -} -``` - -``` json -{ - "uiv": "0.1.0", - "nnc": "12345", - "prf": [{"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"}], - "run": { - "notify-carol": { + "input": { + "value": "hello world" + }, + "prf": [ + { "/": "bafyreieynwqrabzdhgl652ftsk4mlphcj3bxchkj2aw5eb6dc2wxieilau" } + ] + }, + "bafyreigbtlydjneybqpy6r3u5hobsempr335tjw4ozm2aun5yrcj2whdgi": [ + { "/": "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm" }, + { "/": "bafyreicwrzsyonu4efnwtmwqax2s2fbv5padbyk66zwl6kkvrkrd5qavpy" } + ], + "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm": { + "v": "0.1.0", "with": "mailto://alice@example.com", "do": "msg/send", - "inputs": { - "to": "carol@example.com", - "subject": "Hey Carol, DNSLink was updated!", - "body": {"promise/ok": [{"/": "bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu"}, "update-dns"]} + "input": { + "body": { + "await/ok": { + "ucan/task": { + "/": "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli" + } + } + }, + "subject": "DNSLink for example.com", + "to": "bob@example.com" + }, + "prf": [ + { "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" } + ] + }, + "bafyreicwrzsyonu4efnwtmwqax2s2fbv5padbyk66zwl6kkvrkrd5qavpy": { + "scope": [ + { "/": "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli" }, + { "/": "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm" } + ], + "s": { + "/": { + "bytes": "7aEDQKrLlk87jEJh2ftRMXidqypRf2QtwFUvcTWvKN9G1D5XeLB8o6TtPv2qTF/b+s9r6DAQAavilk8J10yFYDyMUAA" + } } } }, - "sig": {"/": {"bytes": "XZRSmp5cHaXX6xWzSTxQqC95kQHtTruysx4S8SrvSjTwr6ttTLzc7dd7atANUQJXoWThUiVuCHWdMnQNQJgiJi"}} + "roots": [ + { "/": "bafyreid5ltmhwuhgcbxa3dwd5erymqdpfsyhlwudlfylu3wrzy6fjbvuoy" }, + { "/": "bafyreigbtlydjneybqpy6r3u5hobsempr335tjw4ozm2aun5yrcj2whdgi" } + ] } ``` -``` json +```json { - "uiv": "0.1.0", - "nnc": "02468", - "prf": [{"/": "bafkreibbz5pksvfjyima4x4mduqpmvql2l4gh5afaj4ktmw6rwompxynx4"}], - "run": { - "notify-bob": { + "blocks": { + "bafyreif4zur3xni5fal23ybxjsw4bm5ezfjp6xgfwjd5ndextr55kr4i34": [ + { "/": "bafyreigl5x2p3ehppg7xbwexdsr63ljfkcr4oj76lwacwj4rt5vfs2cvky" }, + { "/": "bafyreiacpop3w22qc722sdovynbwi2so6r2xetqx5hf4xz326iu3we5sqa" } + ], + "bafyreigl5x2p3ehppg7xbwexdsr63ljfkcr4oj76lwacwj4rt5vfs2cvky": { + "v": "0.1.0", "with": "mailto://alice@example.com", "do": "msg/send", - "inputs": { - "to": "bob@example.com", - "subject": "DNSLink for example.com", - "body": {"promise/ok": [{"/": "bafkreieimb4hvcwizp74vu4xfk34oivbdojzqrbpg2y3vcboqy5hwblmeu"}, "update-dns"]} - } + "input": { + "to": "carol@example.com", + "subject": "Hey Carol, DNSLink was updated!", + "body": { + "await/ok": { + "ucan/invocation": { + "/": "bafyreid5ltmhwuhgcbxa3dwd5erymqdpfsyhlwudlfylu3wrzy6fjbvuoy" + } + } + } + }, + "prf": [ + { "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" } + ] }, - "log-as-done": { + "bafyreibvwhdqydz4qq3narocqsvnqbxoegxgmmlp35mewn53rjxeltfccu": [ + { "/": "bafyreifaip4infpqh76r7nlscubkfznhoiko224i2sthy7w3atfsmrff2a" }, + { "/": "bafyreiacpop3w22qc722sdovynbwi2so6r2xetqx5hf4xz326iu3we5sqa" } + ], + "bafyreifaip4infpqh76r7nlscubkfznhoiko224i2sthy7w3atfsmrff2a": { + "v": "0.1.0", "with": "https://example.com/report", "do": "crud/update", - "inputs": { + "input": { "payload": { "from": "mailto://alice@exmaple.com", "to": ["bob@exmaple.com", "carol@example.com"], "event": "email-notification" }, "_": [ - {"promise/ok": ["/", "notify-bob"]}, - {"promise/ok": [{"/": "bafkreidcqdxosqave5u5pml3pyikiglozyscgqikvb6foppobtk3hwkjn4"}, "notify-carol"]} + { + "await/ok": { + "ucan/invocation": { + "/": "bafyreigbtlydjneybqpy6r3u5hobsempr335tjw4ozm2aun5yrcj2whdgi" + } + } + }, + { + "await/ok": { + "ucan/task": { + "/": "bafyreifjjvdd6hoi7wjot7tjaubwztqldds332bgq6a4n37d6s5slxkbvy" + } + } + } ] + }, + "prf": [ + { "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" } + ] + }, + "bafyreiacpop3w22qc722sdovynbwi2so6r2xetqx5hf4xz326iu3we5sqa": { + "scope": [ + { "/": "bafyreigl5x2p3ehppg7xbwexdsr63ljfkcr4oj76lwacwj4rt5vfs2cvky" }, + { "/": "bafyreifaip4infpqh76r7nlscubkfznhoiko224i2sthy7w3atfsmrff2a" } + ], + "s": { + "/": { + "bytes": "7aEDQBCcyIzfbMRT/UnUF3LgY7Xp/hhhpJfr4kRuEQEgmxcATSE1iLD7VOLMA0nauhqRM7RFUIGjxt1CAf4tZY+yOQE" + } } } }, - "sig": {"/": {"bytes": "5vNn4--uTeGk_vayyPuNTYJ71Yr2nWkc6AkTv1QPWSgetpsu8SHegWoDakPVTdxkWb6nhVKAz6JdpgnjABppC7"}} + "roots": [ + { "/": "bafyreif4zur3xni5fal23ybxjsw4bm5ezfjp6xgfwjd5ndextr55kr4i34" }, + { "/": "bafyreibvwhdqydz4qq3narocqsvnqbxoegxgmmlp35mewn53rjxeltfccu" } + ] } ``` -# 11 Prior Art +# 6 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. @@ -1261,3 +1581,25 @@ Thanks to [Philipp Krüger](https://github.com/matheus23/) for the enthusiastic 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. + + +[dag-json]: https://ipld.io/docs/codecs/known/dag-json/ +[varsig]: https://github.com/ChainAgnostic/varsig/ +[ipld schema]: https://ipld.io/docs/schemas/ +[ucan-ipld]: https://github.com/ucan-wg/ucan-ipld/ +[ucan]: https://github.com/ucan-wg/spec/ +[dag-cbor]: https://ipld.io/specs/codecs/dag-cbor/spec/ +[car]: https://ipld.io/specs/transport/car/carv1/ +[ipld representation]:https://ipld.io/docs/schemas/features/representation-strategies/ +[lazy-vs-eager]: #112-Lazy-vs-Eager-Evaluation +[invoker]: #211-invoker +[executor]: #212-executor +[task]: #3-task +[authorization]: #4-authorization +[invocation]: #5-Invocation +[result]: #6-Result +[receipt]: #7-receipt +[pipelines]: #8-Pipelines +[promise]: #81-promise +[await]: #82-await +[dataflows]: #83-await From 7f5278f16e98f34783efa201131aa48910f7855e Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 31 Jan 2023 00:02:16 -0800 Subject: [PATCH 129/154] Delete diagrams directory Signed-off-by: Irakli Gozalishvili --- diagrams/batch-pipeline.dot | 14 ---- diagrams/batch-pipeline.svg | 66 ------------------- diagrams/concepts.dot | 19 ------ diagrams/concepts.svg | 121 ----------------------------------- diagrams/serial-pipeline.dot | 31 --------- diagrams/serial-pipeline.svg | 77 ---------------------- 6 files changed, 328 deletions(-) delete mode 100644 diagrams/batch-pipeline.dot delete mode 100644 diagrams/batch-pipeline.svg delete mode 100644 diagrams/concepts.dot delete mode 100644 diagrams/concepts.svg delete mode 100644 diagrams/serial-pipeline.dot delete mode 100644 diagrams/serial-pipeline.svg diff --git a/diagrams/batch-pipeline.dot b/diagrams/batch-pipeline.dot deleted file mode 100644 index 869c45b3..00000000 --- a/diagrams/batch-pipeline.dot +++ /dev/null @@ -1,14 +0,0 @@ -digraph { - splines = ortho - - node [shape = box] - - top -> left -> bottom - top -> right -> bottom - - top [label = "dns:example.com?TYPE=TXT\ncrud/update"] - right [label = "mailto:alice@example.com\nmsg/send\ncarol@example.com"] - - left [label = "mailto:alice@example.com\nmsg/send\nbob@example.com"] - bottom [label = "https://example.com/events\ncrud/create"] -} diff --git a/diagrams/batch-pipeline.svg b/diagrams/batch-pipeline.svg deleted file mode 100644 index b3392f1c..00000000 --- a/diagrams/batch-pipeline.svg +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - -top - -dns:example.com?TYPE=TXT -crud/update - - - -left - -mailto:alice@example.com -msg/send -bob@example.com - - - -top->left - - - - - -right - -mailto:alice@example.com -msg/send -carol@example.com - - - -top->right - - - - - -bottom - -https://example.com/events -crud/create - - - -left->bottom - - - - - -right->bottom - - - - - diff --git a/diagrams/concepts.dot b/diagrams/concepts.dot deleted file mode 100644 index 5a51f69d..00000000 --- a/diagrams/concepts.dot +++ /dev/null @@ -1,19 +0,0 @@ -digraph { - rankdir = "LR" - splines = "line" - - node [ - style = rounded - shape = box - ] - - Closure -> Task [label = "+Meta"] - Task -> Batch [label = "Many"] - Task -> Batch - Task -> Batch - Batch -> Invocation [label = "+Auth"] - Invocation -> Result [label = "Output"] - Result -> Promise [label = "Consume"] - Result -> Receipt [label = "+Attest"] - Receipt -> Memoization [label = "Short-circuit"] -} diff --git a/diagrams/concepts.svg b/diagrams/concepts.svg deleted file mode 100644 index 4aad06a6..00000000 --- a/diagrams/concepts.svg +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - -Closure - -Closure - - - -Task - -Task - - - -Closure->Task - - -+Meta - - - -Batch - -Batch - - - -Task->Batch - - -Many - - - -Task->Batch - - - - - -Task->Batch - - - - - -Invocation - -Invocation - - - -Batch->Invocation - - -+Auth - - - -Result - -Result - - - -Invocation->Result - - -Output - - - -Promise - -Promise - - - -Result->Promise - - -Consume - - - -Receipt - -Receipt - - - -Result->Receipt - - -+Attest - - - -Memoization - -Memoization - - - -Receipt->Memoization - - -Short-circuit - - - diff --git a/diagrams/serial-pipeline.dot b/diagrams/serial-pipeline.dot deleted file mode 100644 index e5add01f..00000000 --- a/diagrams/serial-pipeline.dot +++ /dev/null @@ -1,31 +0,0 @@ -digraph { - splines = ortho - - node [shape = box] - - top -> left - top -> right - right -> bottom:nne - left -> bottom - - top -> hidden -> left [style = invis] - hidden -> right [style = invis] - hidden [style = invis] - {rank = same; hidden right} - - top [label = "dns:example.com?TYPE=TXT\ncrud/update"] - right [label = "mailto:alice@example.com\nmsg/send\ncarol@example.com"] - - subgraph cluster { - style = dashed - - left [label = "mailto:alice@example.com\nmsg/send\nbob@example.com"] - bottom [label = "https://example.com/events\ncrud/create"] - - hidden2 [style = invis] - left:s -> hidden2:n [style = invis] - hidden2:e -> bottom:w [style = invis] - - {rank = same; hidden2 bottom} - } -} diff --git a/diagrams/serial-pipeline.svg b/diagrams/serial-pipeline.svg deleted file mode 100644 index cb93d651..00000000 --- a/diagrams/serial-pipeline.svg +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - -cluster - - - - -top - -dns:example.com?TYPE=TXT -crud/update - - - -left - -mailto:alice@example.com -msg/send -bob@example.com - - - -top->left - - - - - -right - -mailto:alice@example.com -msg/send -carol@example.com - - - -top->right - - - - - - - -bottom - -https://example.com/events -crud/create - - - -left->bottom - - - - - - - -right->bottom:nne - - - - - - - - From 8c5e886bc3e304649949658b9997e4cb41952393 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 31 Jan 2023 00:08:10 -0800 Subject: [PATCH 130/154] fix typos Signed-off-by: Irakli Gozalishvili --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 7afc1d7c..39731302 100644 --- a/README.md +++ b/README.md @@ -620,7 +620,7 @@ The `prf` field MUST contain links to any UCANs that provide the authority to pe ### 3.3.2 Sending Email ```json -`{ +{ "v": "0.1.0", "with": "mailto:akiko@example.com", "do": "msg/send", @@ -777,7 +777,7 @@ The `authorization` field MUST contain a link to the [Authorization] that author } ``` -### 5.3.1 Multilpe Invocations +### 5.3.1 Multiple Invocations ```json { @@ -941,7 +941,7 @@ If `fx` field is omitted, or if field is set to an empty list it denote an end o ### 7.2.4 Linked Receipts -When [Invocation] consisets of multiple steps, each step MAY produce a receipt, each subsequent one SHOULD be chained with previous receipt using `origin` field. +When [Invocation] consists of multiple steps, each step MAY produce a receipt, each subsequent one SHOULD be chained with previous receipt using `origin` field. ### 6.2.4 Metadata Fields @@ -1185,9 +1185,9 @@ A `Promise` of the [Invocation] MUST be a link to that invocation wrapped in the ##### 8.1.2.2 Task Promise -An `Promise` of the [Invocation] that shares [Authorization] with a [Task] referecing it MUST be a link to the invocation [Task] wrapped in the `"ucan/task"` tag. +An `Promise` of the [Invocation] that shares [Authorization] with a [Task] referencing it MUST be a link to the invocation [Task] wrapped in the `"ucan/task"` tag. -Note that [Task]s with the same [Authorization] will be unable to referece [Invocation] since [Authorization] CAN be created after all the [Task]s being authorized. The `"ucan/task"` tagged Promise MAY be used in such cases. +Note that [Task]s with the same [Authorization] will be unable to reference [Invocation] since [Authorization] CAN be created after all the [Task]s being authorized. The `"ucan/task"` tagged Promise MAY be used in such cases. ## 8.2 Await @@ -1209,15 +1209,15 @@ type Await union { The successful output of the [Invocation] MAY be referenced by wrapping a [Promise] in the `"await/ok"` tag. -[Executor] MUST fail [Invocation] that `Await`s succeful output of the failed [Invocation]. +[Executor] MUST fail [Invocation] that `Await`s successful output of the failed [Invocation]. -[Executor] MUST substitute [Task] field set to the [Await] of the succesful [Invocation] with an (unwrapped) `ok` value of the output. +[Executor] MUST substitute [Task] field set to the [Await] of the successful [Invocation] with an (unwrapped) `ok` value of the output. ##### 8.2.2.1 Failure -The failed output of the [Invocation] MAY be renfereced by wrapping a [Promise] in the `"await/error"` tag. +The failed output of the [Invocation] MAY be referenced by wrapping a [Promise] in the `"await/error"` tag. -[Executor] MUST fail [Invocation] that `Await`s failed output of the succesful [Invocation]. +[Executor] MUST fail [Invocation] that `Await`s failed output of the successful [Invocation]. [Executor] MUST substitute [Task] field set to the [Await] of the failed [Invocation] with an (unwrapped) `error` value of the output. From 0e4799231e3991cd27107e01ecd15aced9a49c61 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 31 Jan 2023 00:08:26 -0800 Subject: [PATCH 131/154] add some words to ignored list Signed-off-by: Irakli Gozalishvili --- .github/workflows/words-to-ignore.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 610fe7bd..ddcd7a62 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -87,3 +87,5 @@ unpadded url v0 validator +cryptographic +CBOR From 35e6e20854b219eecfb397c493857d5bc13b20a1 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 31 Jan 2023 00:10:30 -0800 Subject: [PATCH 132/154] fix more typos Signed-off-by: Irakli Gozalishvili --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 39731302..c96b05a9 100644 --- a/README.md +++ b/README.md @@ -1228,7 +1228,7 @@ The [Result] output of the [Invocation] MAY be reference by wrapping a [Promise] [Executor] MUST substitute [Task] field set to the [Await] of the [Invocation] with a `Result` value of the output. -## 8.3 Dataflows +## 8.3 Dataflow Pipelining uses [Await] as inputs to determine the required dataflow graph. The following examples both express the following dataflow graph: @@ -1602,4 +1602,4 @@ Thanks to [Rod Vagg](https://github.com/rvagg/) for the clarifications on IPLD S [pipelines]: #8-Pipelines [promise]: #81-promise [await]: #82-await -[dataflows]: #83-await +[dataflow]: #83-dataflow From ae8345e96f6eec93bf530dc212f13c7168eb2480 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 31 Jan 2023 12:34:39 -0800 Subject: [PATCH 133/154] Apply suggestions from code review Co-authored-by: Brooklyn Zelenka Signed-off-by: Irakli Gozalishvili --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c96b05a9..d9588af3 100644 --- a/README.md +++ b/README.md @@ -377,16 +377,13 @@ A [promise] is a reference to an eventual [Receipt] of an [Invocation]. ## 2.3 IPLD Schema ```ipldsch -type Task struct { +type Task struct { v SemVer - with URI do Ability - - input In (implicit {}) + input Args (implicit {}) meta {String : Any} (implicit {}) nnc string (implicit "") - prf [&UCAN] (implicit []) } From ccd02582fa08d5ae1b9c6cd961bd917be9719989 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 1 Feb 2023 01:58:57 -0800 Subject: [PATCH 134/154] Turn fx into a Promise Signed-off-by: Irakli Gozalishvili --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d9588af3..e235e047 100644 --- a/README.md +++ b/README.md @@ -407,8 +407,8 @@ type Receipt struct { # output of the invocation out Result - # Effects to be performed - fx [&Invocation] (implicit {}) + # effects to be performed + fx optional &Promise # Related receipts origin optional &Receipt @@ -897,7 +897,7 @@ type Receipt struct { # output of the invocation out Result # Effects to be performed - fx [&Invocation] (implicit {}) + fx optional &Promise # Related receipts origin optional &Receipt @@ -930,15 +930,17 @@ The `out` field MUST contain the output of the invocation in [Result] format. ### 7.2.3 Effects -Some [Task]s may describe complex workflows with multiple, sometimes concurrent, steps. Such [Task]s MAY capture each state update using `out` of the chained [Receipt] and consequent (concurrent) steps using `fx` [Invocation]s. +Some [Task]s may represent step in a complex workflow with multiple, sometimes concurrent, steps. Such [Task]s MAY capture state of the workflow in the `out` field of the [Receipt] and specify consequent step(s) using [Promise] in the `fx` field of the [Receipt]. -The OPTIONAL `fx` field, if present MUST contain set of concurrent [Invocation]s that need to be run by the [Executor] to complete execution of the ongoing [Invocation]. [Executor] MUST run contained [Invocation]s concurrently unless they are ordered using promise pipelining. +The OPTIONAL `fx` field, if present MUST contain link to a [Promise]. [Executor] SHOULD perform the [Task] underlying a [Promise] in the `fx` field to progress a workflow execution. -If `fx` field is omitted, or if field is set to an empty list it denote an end of the execution. +If `fx` field is omitted, it denote an completion of the workflow execution. ### 7.2.4 Linked Receipts -When [Invocation] consists of multiple steps, each step MAY produce a receipt, each subsequent one SHOULD be chained with previous receipt using `origin` field. +Some [Invocation]s may represent step in a complex workflow with multiple steps. Each completed step in such a workflow would have corresponding [Receipt]. Each subsequent receipt SHOULD set `origin` field to a [Receipt] link of the preeceding step. This establishes a signed chain of receipts that leading to any one Receipt. + +The `origin` field MUST be omitted for [Invocation]s that represent first or only step in the workflow. ### 6.2.4 Metadata Fields @@ -1025,11 +1027,9 @@ The `s` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without "out": { "ok": {} }, - "fx": [ - { - "/": "bafyreib6mgm5few6pnajc25h6r6trp2kbi6xrmhafnmby3hrflmgql2kna" - } - ], + "fx": { + "/": "bafyreib6mgm5few6pnajc25h6r6trp2kbi6xrmhafnmby3hrflmgql2kna" + }, "s": { "/": { "bytes": "7aEDQMO5k6OtYgHSRVIR2sqn97TTfrhLrdTSusoYOAaCV2lg37xI22QMjMhW44eVgkIRcWcbLO4YdrJfcwG4t3jzegM" From 8e9d3487997197552af5d85170d8685acfaa5088 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Mon, 6 Feb 2023 22:15:51 -0800 Subject: [PATCH 135/154] update per consensus --- .github/workflows/linkcheck.yml | 23 +- .markdownlint.jsonc | 257 +++++++ README.md | 1125 +++++++++++++++++++------------ 3 files changed, 949 insertions(+), 456 deletions(-) create mode 100644 .markdownlint.jsonc diff --git a/.github/workflows/linkcheck.yml b/.github/workflows/linkcheck.yml index 998d61a3..d3833842 100644 --- a/.github/workflows/linkcheck.yml +++ b/.github/workflows/linkcheck.yml @@ -1,15 +1,22 @@ name: Check Markdown links -on: push +on: + push: + branches: + - main + pull_request: jobs: markdown-link-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@master - - uses: gaurav-nelson/github-action-markdown-link-check@v1 - with: - use-quiet-mode: 'yes' - check-modified-files-only: 'yes' - base-branch: 'main' - config-file: './.github/workflows/linkcheck.cfg.json' + - uses: actions/checkout@master + - uses: gaurav-nelson/github-action-markdown-link-check@v1 + with: + use-quiet-mode: "yes" + check-modified-files-only: "yes" + base-branch: "main" + config-file: "./.github/workflows/linkcheck.cfg.json" + - uses: DavidAnson/markdownlint-cli2-action@v9 + with: + globs: "*.md" diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc new file mode 100644 index 00000000..32df054e --- /dev/null +++ b/.markdownlint.jsonc @@ -0,0 +1,257 @@ +{ + // Example markdownlint JSON(C) configuration with all properties set to their default value + + // Default state for all rules + "default": true, + + // Path to configuration file to extend + "extends": null, + + // MD001/heading-increment/header-increment - Heading levels should only increment by one level at a time + "MD001": true, + + // MD002/first-heading-h1/first-header-h1 - First heading should be a top-level heading + "MD002": { + // Heading level + "level": 1 + }, + + // MD003/heading-style/header-style - Heading style + "MD003": { + // Heading style + "style": "consistent" + }, + + // MD004/ul-style - Unordered list style + "MD004": { + // List style + "style": "consistent" + }, + + // MD005/list-indent - Inconsistent indentation for list items at the same level + "MD005": true, + + // MD006/ul-start-left - Consider starting bulleted lists at the beginning of the line + "MD006": true, + + // MD007/ul-indent - Unordered list indentation + "MD007": { + // Spaces for indent + "indent": 2, + // Whether to indent the first level of the list + "start_indented": false, + // Spaces for first level indent (when start_indented is set) + "start_indent": 2 + }, + + // MD009/no-trailing-spaces - Trailing spaces + "MD009": { + // Spaces for line break + "br_spaces": 2, + // Allow spaces for empty lines in list items + "list_item_empty_lines": false, + // Include unnecessary breaks + "strict": false + }, + + // MD010/no-hard-tabs - Hard tabs + "MD010": { + // Include code blocks + "code_blocks": true, + // Fenced code languages to ignore + "ignore_code_languages": [], + // Number of spaces for each hard tab + "spaces_per_tab": 1 + }, + + // MD011/no-reversed-links - Reversed link syntax + "MD011": true, + + // MD012/no-multiple-blanks - Multiple consecutive blank lines + "MD012": { + // Consecutive blank lines + "maximum": 1 + }, + + "MD013": false, + + // MD014/commands-show-output - Dollar signs used before commands without showing output + "MD014": true, + + // MD018/no-missing-space-atx - No space after hash on atx style heading + "MD018": true, + + // MD019/no-multiple-space-atx - Multiple spaces after hash on atx style heading + "MD019": true, + + // MD020/no-missing-space-closed-atx - No space inside hashes on closed atx style heading + "MD020": true, + + // MD021/no-multiple-space-closed-atx - Multiple spaces inside hashes on closed atx style heading + "MD021": true, + + // MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines + "MD022": { + // Blank lines above heading + "lines_above": 1, + // Blank lines below heading + "lines_below": 1 + }, + + // MD023/heading-start-left/header-start-left - Headings must start at the beginning of the line + "MD023": true, + + // MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content + "MD024": { + // Only check sibling headings + "allow_different_nesting": false, + // Only check sibling headings + "siblings_only": false + }, + + // MD025/single-title/single-h1 - Multiple top-level headings in the same document + "MD025": false, + + // MD026/no-trailing-punctuation - Trailing punctuation in heading + "MD026": { + // Punctuation characters not allowed at end of headings + "punctuation": ".,;:!。,;:!" + }, + + // MD027/no-multiple-space-blockquote - Multiple spaces after blockquote symbol + "MD027": true, + + // MD028/no-blanks-blockquote - Blank line inside blockquote + "MD028": true, + + // MD029/ol-prefix - Ordered list item prefix + "MD029": { + // List style + "style": "one_or_ordered" + }, + + // MD030/list-marker-space - Spaces after list markers + "MD030": { + // Spaces for single-line unordered list items + "ul_single": 1, + // Spaces for single-line ordered list items + "ol_single": 1, + // Spaces for multi-line unordered list items + "ul_multi": 1, + // Spaces for multi-line ordered list items + "ol_multi": 1 + }, + + // MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines + "MD031": { + // Include list items + "list_items": true + }, + + // MD032/blanks-around-lists - Lists should be surrounded by blank lines + "MD032": true, + + // MD033/no-inline-html - Inline HTML + "MD033": { + // Allowed elements + "allowed_elements": [] + }, + + // MD034/no-bare-urls - Bare URL used + "MD034": true, + + // MD035/hr-style - Horizontal rule style + "MD035": { + // Horizontal rule style + "style": "consistent" + }, + + // MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading + "MD036": { + // Punctuation characters + "punctuation": ".,;:!?。,;:!?" + }, + + // MD037/no-space-in-emphasis - Spaces inside emphasis markers + "MD037": true, + + // MD038/no-space-in-code - Spaces inside code span elements + "MD038": true, + + // MD039/no-space-in-links - Spaces inside link text + "MD039": true, + + // MD040/fenced-code-language - Fenced code blocks should have a language specified + "MD040": { + // List of languages + "allowed_languages": [], + // Require language only + "language_only": false + }, + + // MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading + "MD041": { + // Heading level + "level": 1, + // RegExp for matching title in front matter + "front_matter_title": "^\\s*title\\s*[:=]" + }, + + // MD042/no-empty-links - No empty links + "MD042": true, + + // MD043/required-headings/required-headers - Required heading structure + "MD043": false, + + // MD044/proper-names - Proper names should have the correct capitalization + "MD044": { + // List of proper names + "names": [], + // Include code blocks + "code_blocks": true, + // Include HTML elements + "html_elements": true + }, + + // MD045/no-alt-text - Images should have alternate text (alt text) + "MD045": true, + + // MD046/code-block-style - Code block style + "MD046": { + // Block style + "style": "consistent" + }, + + // MD047/single-trailing-newline - Files should end with a single newline character + "MD047": true, + + // MD048/code-fence-style - Code fence style + "MD048": { + // Code fence style + "style": "consistent" + }, + + // MD049/emphasis-style - Emphasis style should be consistent + "MD049": { + // Emphasis style should be consistent + "style": "consistent" + }, + + // MD050/strong-style - Strong style should be consistent + "MD050": { + // Strong style should be consistent + "style": "consistent" + }, + + // MD051/link-fragments - Link fragments should be valid + "MD051": true, + + // MD052/reference-links-images - Reference links and images should use a label that is defined + "MD052": true, + + // MD053/link-image-reference-definitions - Link and image reference definitions should be needed + "MD053": { + // Ignored definitions + "ignored_definitions": ["//"] + } +} diff --git a/README.md b/README.md index e235e047..3b0ea2d7 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Most languages use eager evaluation. Eager languages must contend directly with ```js const message = () => alert("hello world") message // Nothing happens -message() // A message interups the user +message() // A message interrupts the user ``` Delegating a capability is like the statement `message`. Task is akin to `message()`. It's true that sometimes we know to run things from their surrounding context without the parentheses: @@ -73,7 +73,7 @@ Information about the scheduling, order, and pipelining of tasks is orthogonal t 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). -``` +```txt ────────────────────────────────────────────Time──────────────────────────────────────────────────────► ┌──────────────────────────────────────────Delegation─────────────────────────────────────────────────────┐ @@ -120,38 +120,20 @@ As we shall see in the [discussion of promise pipelining][pipelines], asking an 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 -// CID {"/": "Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD"} -// Bytes (e.g. a signature) +### 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 Note on Schema Syntax - -We use [IPLD Schema] syntax extended with generics. Standard IPLD Schema can be derived by ignoring parameters enclosed in angle brackets and interpreting parameters as `any`. - -Below schema is in the extended syntax - -```ipldsch -type Box struct { - value T -} -``` - -It compiles down to a following standard syntax - -```ipldsch -type Box struct { - value Box_T -} -type Box_T any -``` - -## 1.5 Signatures +## 1.4 Signatures All payloads described in this spec MUST be signed with a [Varsig]. @@ -191,7 +173,6 @@ send.task: Task send.promise: Await read.promise: Await send.receipt: Receipt - read: Invocation read.task: Task @@ -214,7 +195,7 @@ state send.task { send.task.input: input send.task.nnc: nnc send.task.meta: meta - + send.do: msg/send send.with: mailto꞉//alice@example.com @@ -273,17 +254,19 @@ state update.task { } state send { - send.run: task + send.run: run send.auth: authorization } state read { - read.run: task + read.run: run + read.cause: cause read.auth: authorization } state update { - update.run: task + update.run: run + update.cause: cause update.auth: authorization } @@ -296,6 +279,7 @@ state read.promise { state read.promise.await <> } + state send.receipt { send.receipt.sig: sig send.receipt.ran: ran @@ -312,6 +296,10 @@ state send.receipt { send.result.ok: success send.result.error: { error } + -- + + send.fx.fork: fork + send.fx.join: join } @@ -347,6 +335,12 @@ send.receipt.sig --> send.receipt.iss send.receipt.sig --> send.receipt.prf +send.receipt.fx --> send.fx.fork +send.receipt.fx --> send.fx.join +send.fx.fork --> update.task +send.fx.fork --> read.task +read.cause --> send +update.cause --> send send.receipt.ran --> send ``` @@ -360,7 +354,7 @@ An [Authorization] is a cryptographically signed proof permitting execution of r ### 2.2.3 Invocation -An [Invocation] is an invoker authorized instruction to an executor to run the [Task]. +An [Invocation] is an invoker authorized instruction to the [Executor] to run the [Task]. ### 2.2.4 Result @@ -370,51 +364,58 @@ A [Result] is the output of a [Task]. A [Receipt] is a cryptographically signed description of the [Invocation] output. -### 2.2.6 Promise +### 2.2.6 Effect -A [promise] is a reference to an eventual [Receipt] of an [Invocation]. +An [Effect] are the instruction to the [Executor] to run set of [Task]s concurrently. ## 2.3 IPLD Schema ```ipldsch -type Task struct { - v SemVer +type Task struct { with URI do Ability - input Args (implicit {}) - meta {String : Any} (implicit {}) - nnc string (implicit "") - prf [&UCAN] (implicit []) + input {String: any} (implicit "{}") + + nnc string (implicit "''") } -type SemVer string type URI string +type Ability string type Authorization struct { # Authorization is denoted by the set of links been authorized - scope [&Any] (implicit []) + scope [&Any] (implicit "[]") # Scope signed by the invoker s VarSig } -type Invocation struct { - task &Task - authorization &Authorization -} representation tuple +type Invocation struct { + v SemVer -type Receipt struct { - ran &Invocation + run &Task + # Receipt of the invocation that caused this invocation + cause optional &Invocation + # Task authorization. + auth &Authorization - # output of the invocation - out Result - # effects to be performed - fx optional &Promise + meta {String : any} (implicit "{}") - # Related receipts - origin optional &Receipt + prf [&UCAN] (implicit "[]") +} +type SemVer string + +type Receipt struct { + # Invocation this is a receipt for + ran &Invocation + + # Output of the invocation + out Result + + # Effects to be performed + fx Effect (implicit "{}") # All the other metadata - meta { String: Any } (implicit {}) + meta {String: any} (implicit "{}") # Principal that issued this receipt. If omitted issuer is # inferred from the invocation task audience. @@ -422,27 +423,49 @@ type Receipt struct { # When issuer is different from executor this MUST hold a UCAN # delegation chain from executor to the issuer. Should be omitted executor is an issuer. - prf [&UCAN] implicit ([]) + prf [&UCAN] (implicit "[]") - # Signature from the `iss`. + # Signature from the "iss". s Varsig } -type Result union { - | Ok ("ok") # Success - | Error ("error") # Error +type Result union { + | any "ok" # Success + | any "error" # Error } representation kinded -type Promise union { - | &Task ("ucan/task") - | &Invocation ("ucan/invocation") -} representation keyed +# Represents a request to invoke enclosed set of tasks concurrently +type Effect struct { + # Primary set of tasks to be invoked + fork [&Invocation] (implicit "[]") + + # Additional task to be invoked with added semantics + # of representing a workflow execution continuation. + join optional &Promise +} + +# Promise is an Invocation with optional 'auth' field which if omitted +# is implicitly an 'auth' of the Invocation that contains Await. +type Promise struct { + v SemVer + + run &Task + # Receipt of the invocation that caused this invocation + cause optional &Invocation + # Task authorization. If omitted can be interpreted as requires + # authorization + auth optional &Authorization -# Promise is a way to reference invocation receipt -type Await union { - &Promise "await/*" - &Promise "await/ok" - &Promise "await/error" + meta {String : any} (implicit "{}") + + prf [&UCAN] (implicit "[]") +} + +# Promise is a way to reference result of the invocation +type Await union { + | &Promise "await/*" + | &Promise "await/ok" + | &Promise "await/error" } representation keyed ``` @@ -454,91 +477,79 @@ Using the JavaScript analogy from the introduction, a Task is similar to wrappin ```json { - "v": "0.1.0", - "with": "mailto://alice@example.com", "do": "msg/send", + "with": "mailto:alice@example.com", "input": { - "to": ["bob@example.com", "carol@example.com"], + "to": [ + "bob@example.com", + "carol@example.com" + ], "subject": "hello", "body": "world" - }, - "prf": [ - { "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" } - ] + } } ``` ```js // Pseudocode -() => msg.send("mailto:alice@example.com", { - to: ["bob@example.com", "carol@example.com"], - subject: "hello", - body: "world" -}) +() => + msg.send("mailto:alice@example.com", { + to: ["bob@example.com", "carol@example.com"], + subject: "hello", + body: "world" + }) ``` -Later, when we explore [Promise]s, this also includes capturing the promise: +Later, when we explore promise [pipelines], this also includes capturing the promise: ```json { - "bafyreihroupaenuxjduzs4ynympv3uawcct4mj4sa7ciuqqlss4httehiu": { - "v": "0.1.0", + "bafyreid32aunjg6g6buib7bva2sa5ni4c6eaftgykyh6z6rnhcjxh4yk2y": { "do": "crud/read", - "with": "https://example.com/mailinglist", - "prf": [ - { "/": "bafyreib2dldcev74g5i76bacd4w72gowuraouv2kvnwa2ympvrvtybcsfi" } - ] + "with": "https://exmaple.com/mailinglist" }, - "bafyreigiz22kfo4bbrsl3jyspm5ykuh2jk5hxmduc5yzijp3dzegvvi6tq": { - "v": "0.1.0", - "with": "mailto://alice@example.com", + "bafyreigfusg7tgegda7pxdn7px5vs7wxqk5nybr5ewjht4xas357x6ryia": { "do": "msg/send", + "with": "mailto://alice@example.com", "input": { "to": { - "await/*": { - "ucan/task": { - "/": "bafyreihroupaenuxjduzs4ynympv3uawcct4mj4sa7ciuqqlss4httehiu" - } + "await/ok": { + "/": "bafyreifkeu34pzda27fozfcboh25psekhqehkvnekltoiaspb5jsp6pj5q" } }, "subject": "hello", "body": "world" - }, - "prf": [ - { "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" } - ] + } } } ``` ```js // Pseudocode -const mailingList = crud.read("https://exmaple.com/mailinglist") -msg.send("mailto:alice@example.com", { - to: (await mailingList).ok, +const mailingList = crud.read("https://exmaple.com/mailinglist"); +const sendEmail = msg.send("mailto://alice@example.com", { + to: mailingList.await().ok, subject: "hello", - body: "world", -}) + body: "world" +}); ``` ## 3.1 Schema ```ipldsch -type Task struct { +type Task struct { v SemVer - with URI do Ability - - input In (implicit {}) - meta {String : Any} (implicit {}) - nnc string (implicit "") - - prf [&UCAN] (implicit []) + input {String:any} (implicit "{}") + meta {String:any} (implicit "{}") + nnc string (implicit "''") + prf [&UCAN] (implicit "[]") } -type SemVer string type URI string +type SemVer string + ``` ## 3.2 Fields @@ -564,14 +575,12 @@ If present, `input` field MUST have an IPLD [map representation][ipld representa 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. -3. [map](https://ipld.io/docs/schemas/features/representation-strategies/#map-map-representation) in 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. If `input` field is not present, it is implicitly a `unit` represented as empty map. - ### 3.2.5 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. @@ -582,21 +591,18 @@ If `meta` field is not present, it is implicitly a `unit` represented as an empt If present, the OPTIONAL `nnc` field MUST include a random nonce expressed in ASCII. This field ensures that multiple invocations are unique. - ### 3.2.7 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. - ## 3.3 DAG-JSON Examples ### 3.3.1 Interacting with an HTTP API ```json { - "v": "0.1.0", - "with": "https://example.com/blog/posts", "do": "crud/create", + "with": "https://example.com/blog/posts", "input": { "headers": { "content-type": "application/json" @@ -604,13 +610,13 @@ The `prf` field MUST contain links to any UCANs that provide the authority to pe "payload": { "title": "How UCAN Tasks Changed My Life", "body": "This is the story of how one spec changed everything...", - "topics": ["authz", "journal"], + "topics": [ + "authz", + "journal" + ], "draft": true } - }, - "prf": [ - { "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" } - ] + } } ``` @@ -618,21 +624,16 @@ The `prf` field MUST contain links to any UCANs that provide the authority to pe ```json { - "v": "0.1.0", - "with": "mailto:akiko@example.com", "do": "msg/send", + "with": "mailto:akiko@example.com", "input": { - "to": ["boris@example.com", "carol@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 Tasks!" - }, - "meta": { - "dev/priority": "high", - "dev/tags": ["friends", "coffee"] - }, - "prf": [ - { "/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4" } - ] + } } ``` @@ -640,22 +641,19 @@ The `prf` field MUST contain links to any UCANs that provide the authority to pe ```json { - "v": "0.1.0", - "with": "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==", "do": "wasm/run", + "with": "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==", "input": { "func": "add_one", - "args": [42] - }, - "prf": [ - { "/": "bafyreibrqkoin6jzc35hnbrfbsenmcyvd26bn3xyq4of6iwk5z4h63qr34" } - ] + "args": [ + 42 + ] + } } ``` # 4 Authorization - An [Authorization] is cryptographically signed data set. It represents an authorization to run [Task]s in that are included in `scope` data set. ## 4.1 Schema @@ -668,8 +666,8 @@ type Authorization struct { s VarSig } ``` -### 4.2 Fields +### 4.2 Fields #### 4.2.1 Authorization Scope @@ -677,71 +675,79 @@ The `scope` field MUST be a set of links been authorized. It SHOULD be encoded a If `scope` field is omitted, it is implicitly a an empty list and has no practical use as it authorizes nothing. - ### 4.2.2 Signature The `s` field MUST contain a [Varsig] of the [CBOR] encoded `scope` field. ## 4.3 DAG-JSON Example - ```json { "scope": [ - { "/": "bafyreihroupaenuxjduzs4ynympv3uawcct4mj4sa7ciuqqlss4httehiu" }, - { "/": "bafyreigiz22kfo4bbrsl3jyspm5ykuh2jk5hxmduc5yzijp3dzegvvi6tq" } + { + "/": "bafyreigfusg7tgegda7pxdn7px5vs7wxqk5nybr5ewjht4xas357x6ryia" + }, + { + "/": "bafyreid32aunjg6g6buib7bva2sa5ni4c6eaftgykyh6z6rnhcjxh4yk2y" + } ], "s": { "/": { - "bytes": "7aEDQBzEB/4ViFVHUelItLNcVbXOQEx9rWmfTfoIVnA4QtL08yA3fPcXZd0kRE3Qr0BD61es4R/XHM/yK+kX1PfiWAk" + "bytes": "7aEDQJBsO/9rkcgQQ9qvSC0E60MdqGjpIHXuc4VbkfjEeYR7iAvAY+0QEN9kAFcwh6/kww4bK/I7xsqLzO5kG0x+2Qs" } - }, + } } ``` # 5 Invocation -As [noted in the introduction][lazy-vs-eager], there is a difference between a reference to a function and calling that function. [Tasks] are not executable until they have been authorized by [Invoker] using [Authorization]. +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]. -Invocation describes `(task, authorization)` tuple and instructs [Executor] to run the [Task]. - -The `authorization` MUST authorize the `task` by including link to it in it's `scope` field. The Invocation of the [Task] that is not included in the linked [Authorization] `scope` MUST be considered invalid. +If `auth` field MUST be set to the [Authorization] (link) which contains `run` in it's scope field. The Invocation of the [Task] with an [Authorization] which does not includes [Task] in the `scope` MUST be considered invalid. ## 5.1 Schema ```ipldsch type Invocation struct { - task &Task - authorization &Authorization -} representation tuple + v SemVer + + run &Task + # Receipt of the invocation that caused this invocation + cause optional &Invocation + # Task authorization. + auth &Authorization + + meta {String : any} (implicit "{}") + + prf [&UCAN] (implicit "[]") +} +type SemVer string ``` ## 5.2 Fields -### 5.2.1 +### 5.2.1 Task -The `task` field MUST contain a link to the [Task] to be run. +The `run` field MUST contain a link to the [Task] to be run. -### 5.2.2 +### 5.2.2 Authorization -The `authorization` field MUST contain a link to the [Authorization] that authorizes invoked `task`. +The `auth` field MUST contain a link to the [Authorization] that authorizes invoked [Task] in the `run` field. Linked [Authorization] MUST contain `run` in it's `scope`. + +### 5.2.3 Cause + +Some [Task]s may be invoked as an effect caused by another [Task] [Invocation]. Such Invocations SHOULD have `cause` field set to the [Invocation] (link) that caused it. The [Receipt] of the causing [Invocation] (Invocation linked from `cause` field) SHOULD have an `Effect` (the `fx` field) containing cased [Invocation]. ## 5.3 DAG-JSON Example ### 5.3.1 Single Invocation - ```json { "blocks": { - "bafyreihwj7mouljcwl7s7xpp6sfexptedrmkraoro2tcgiyu4jnjg2rauu": [ - { "/": "bafyreiduxn4l73hs5abjjgpyyazmgar7fd64pdlj62hofr2qm7prnqkwuu" }, - { "/": "bafyreibgo4bq2kxf4ykwz7c4zyvulfytwn46xk5ml2c7tq57fp6d7mhjvq" } - ], - "bafyreiduxn4l73hs5abjjgpyyazmgar7fd64pdlj62hofr2qm7prnqkwuu": { - "v": "0.1.0", - "with": "https://example.com/blog/posts", + "bafyreibgqjpwjks2dh2zgvq5ypuh5bly6quoi2dxjzfoa676owl6tscz5q": { "do": "crud/create", + "with": "https://example.com/blog/posts", "input": { "headers": { "content-type": "application/json" @@ -749,27 +755,45 @@ The `authorization` field MUST contain a link to the [Authorization] that author "payload": { "title": "How UCAN Tasks Changed My Life", "body": "This is the story of how one spec changed everything...", - "topics": ["authz", "journal"], + "topics": [ + "authz", + "journal" + ], "draft": true } + } + }, + "bafyreifpdtrqxeqvg5r5ctvcihnsrregsnw3gi74unbl66yf3dio2fee4a": { + "v": "0.1.0", + "run": { + "/": "bafyreibgqjpwjks2dh2zgvq5ypuh5bly6quoi2dxjzfoa676owl6tscz5q" + }, + "auth": { + "/": "bafyreifpvyetnerhqoqdijxx4kf6hhofaz7momudq52scepsxzksshxm5a" }, "prf": [ - { "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" } + { + "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" + } ] }, - "bafyreibgo4bq2kxf4ykwz7c4zyvulfytwn46xk5ml2c7tq57fp6d7mhjvq": { + "bafyreifpvyetnerhqoqdijxx4kf6hhofaz7momudq52scepsxzksshxm5a": { "scope": [ - { "/": "bafyreiduxn4l73hs5abjjgpyyazmgar7fd64pdlj62hofr2qm7prnqkwuu" } + { + "/": "bafyreibgqjpwjks2dh2zgvq5ypuh5bly6quoi2dxjzfoa676owl6tscz5q" + } ], "s": { "/": { - "bytes": "7aEDQIUWpsYzEkIX8gjunh81kgGFW9KYlEOewghwmowQRCuhkQsxZmpymmfsXVFSL6m79O1s5c+G2pgqODu2qUM4nAY" + "bytes": "7aEDQDqN6XjxQnEXQLlg03zRXpoIpA/0ldLCVHA0hDdJLLQlRvQoh4Q24eAL3mozij08vTcKQhkvqClAuJ9FJr1eNAo" } } } }, "roots": [ - { "/": "bafyreihwj7mouljcwl7s7xpp6sfexptedrmkraoro2tcgiyu4jnjg2rauu" } + { + "/": "bafyreifpdtrqxeqvg5r5ctvcihnsrregsnw3gi74unbl66yf3dio2fee4a" + } ] } ``` @@ -779,14 +803,9 @@ The `authorization` field MUST contain a link to the [Authorization] that author ```json { "blocks": { - "bafyreib527h7rxykieyccwqckfvbrxfwkf6dbiwnilakf7ha3rlofeazdy": [ - { "/": "bafyreiduxn4l73hs5abjjgpyyazmgar7fd64pdlj62hofr2qm7prnqkwuu" }, - { "/": "bafyreigdxhwdln62fekut623s5hipnhkgsc7nurtg4utsrlbt6di4dyptm" } - ], - "bafyreiduxn4l73hs5abjjgpyyazmgar7fd64pdlj62hofr2qm7prnqkwuu": { - "v": "0.1.0", - "with": "https://example.com/blog/posts", + "bafyreibgqjpwjks2dh2zgvq5ypuh5bly6quoi2dxjzfoa676owl6tscz5q": { "do": "crud/create", + "with": "https://example.com/blog/posts", "input": { "headers": { "content-type": "application/json" @@ -794,50 +813,127 @@ The `authorization` field MUST contain a link to the [Authorization] that author "payload": { "title": "How UCAN Tasks Changed My Life", "body": "This is the story of how one spec changed everything...", - "topics": ["authz", "journal"], + "topics": [ + "authz", + "journal" + ], "draft": true } - }, - "prf": [ - { "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" } - ], + } }, - "bafyreiefdnvc5xuakriluhev5gpqephudvkkwhrbo6ixz5cocvi3camsra": [ - { "/": "bafyreicewv7jbynidkzljwc3okytusrljgjpn7xjamrrcnokgp5qei77gy" }, - { "/": "bafyreigdxhwdln62fekut623s5hipnhkgsc7nurtg4utsrlbt6di4dyptm" } - ], - "bafyreicewv7jbynidkzljwc3okytusrljgjpn7xjamrrcnokgp5qei77gy": { - "v": "0.1.0", - "with": "mailto:akiko@example.com", + "bafyreiefy3rmuhla7uoxsjbv2zmz2ysmgy3erigwiyckcglxes6sgywms4": { "do": "msg/send", + "with": "mailto:akiko@example.com", "input": { - "to": ["boris@example.com", "carol@example.com"], + "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" + } + }, + "bafyreiaf4vq3ia7ykisqaxb3oxxjgam226b5juzia6xmrpjgodpief42cq": { + "v": "0.1.0", + "run": { + "/": "bafyreibgqjpwjks2dh2zgvq5ypuh5bly6quoi2dxjzfoa676owl6tscz5q" }, - "meta": { - "dev/priority": "high", - "dev/tags": ["friends", "coffee"] + "auth": { + "/": "bafyreiatkpx7p4m2jlry547in4asajlyqncmvpiykprgyd6uqfwlyvfirm" }, "prf": [ - { "/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4" } - ], + { + "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" + } + ] }, - "bafyreigdxhwdln62fekut623s5hipnhkgsc7nurtg4utsrlbt6di4dyptm": { - "scope": [ - { "/": "bafyreiduxn4l73hs5abjjgpyyazmgar7fd64pdlj62hofr2qm7prnqkwuu" }, - { "/": "bafyreicewv7jbynidkzljwc3okytusrljgjpn7xjamrrcnokgp5qei77gy" } + "bafyreifpjetehajrap63g45djib2mxdzlr5wl442kbqgwpozfoiz76535u": { + "v": "0.1.0", + "run": { + "/": "bafyreiefy3rmuhla7uoxsjbv2zmz2ysmgy3erigwiyckcglxes6sgywms4" + }, + "auth": { + "/": "bafyreiatkpx7p4m2jlry547in4asajlyqncmvpiykprgyd6uqfwlyvfirm" + }, + "prf": [ + { + "/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4" + } ] + }, + "bafyreiatkpx7p4m2jlry547in4asajlyqncmvpiykprgyd6uqfwlyvfirm": { + "scope": [ + { + "/": "bafyreiefy3rmuhla7uoxsjbv2zmz2ysmgy3erigwiyckcglxes6sgywms4" + }, + { + "/": "bafyreibgqjpwjks2dh2zgvq5ypuh5bly6quoi2dxjzfoa676owl6tscz5q" + } + ], "s": { "/": { - "bytes": "7aEDQIU6IPBZ8JMaB7mmTAwmIpMR4qMGUdD8R/cU8rc5uzxSQhetKw1dFdePqbffdC6aKdifv5MXO0tA2iKfN7tkhwI" + "bytes": "7aEDQFbdUsz5BvcCGkzwVtthNoQI/bxXi2CO+bmlFIbmfll/5+40dadU6sIY2vUfxYgUywprct3H+yIYIp0HuNdgxQM" } + } + } + }, + "roots": [ + { + "/": "bafyreiaf4vq3ia7ykisqaxb3oxxjgam226b5juzia6xmrpjgodpief42cq" + }, + { + "/": "bafyreifpjetehajrap63g45djib2mxdzlr5wl442kbqgwpozfoiz76535u" + } + ] +} +``` + +### 5.3.3 Causal Invocations + +```json +{ + "blocks": { + "bafyreifcerdvicarktlnif5uj25ultgpedwg63nxhmp7anoepaamqj4eji": { + "do": "crud/update", + "with": "dns:example.com?TYPE=TXT", + "input": { + "value": "hello world" + } + }, + "bafyreicc3rilzaxravqju766yr4v7p6nm6sjsd4lbkfa4uqohbbxaejh7e": { + "v": "0.1.0", + "run": { + "/": "bafyreifcerdvicarktlnif5uj25ultgpedwg63nxhmp7anoepaamqj4eji" + }, + "auth": { + "/": "bafyreifw2hxvz66fxx7vsn6q3ayrc62smhvt7jta3ck2u6kf4xgqo524hm" }, + "cause": { + "/": "bafyreiekrhjlfend2mwcmtakwshuctotwqcm2wriya6thpjcgvjzfmkt24" + }, + "prf": [ + { + "/": "bafyreieynwqrabzdhgl652ftsk4mlphcj3bxchkj2aw5eb6dc2wxieilau" + } + ] + }, + "bafyreifw2hxvz66fxx7vsn6q3ayrc62smhvt7jta3ck2u6kf4xgqo524hm": { + "scope": [ + { + "/": "bafyreifcerdvicarktlnif5uj25ultgpedwg63nxhmp7anoepaamqj4eji" + } + ], + "s": { + "/": { + "bytes": "7aEDQEkocMddH2BF4wuMRSlJaJFhu7pQaAInuzLrfU3TQd9XV/Rd0I6N6OvxPaCebNfpuCXrAgtlAxVh+sn8gWSSegk" + } + } } }, "roots": [ - { "/": "bafyreib527h7rxykieyccwqckfvbrxfwkf6dbiwnilakf7ha3rlofeazdy" }, - { "/": "bafyreiefdnvc5xuakriluhev5gpqephudvkkwhrbo6ixz5cocvi3camsra" } + { + "/": "bafyreicc3rilzaxravqju766yr4v7p6nm6sjsd4lbkfa4uqohbbxaejh7e" + } ] } ``` @@ -849,9 +945,10 @@ A `Result` records the output of the [Task], as well as its success or failure s ## 6.1 Schema ```ipldsch -type Result union { - | Ok ("ok") # Success - | Error ("error") # Error +type Result union { + | any "ok" + | any "error" +} representation keyed } ``` @@ -862,7 +959,7 @@ type Result union { 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 -{"ok": 42} +{ "ok": 42 } ``` ## 6.2.2 Failure @@ -880,30 +977,110 @@ If no information is available, this field SHOULD be set to `{}`. } ``` -# 7 Receipt +# 7 Effect + +Workflows often consist of many, sometimes concurrent, steps which form an execution threads. Steps of such workflows are discrete [Task] [Invocation]s, each producing some [Result] and a description of subsequent step(s) caused by it. Subsequent steps are denoted with an `Effect`, a set of [Invocation]s that [Executor] must perform to advance workflow execution. + +All of the [Invocation]s of the `Effect` MUST be concurrent with one another, unless explicitly arranged differently using [Pipelines]. + +Effect MAY instruct the [Executor] to run set of [Task] [Invocation]s in concurrent execution threads by providing them as list under `fork` field. + +Effect MAY instruct the [Executor] that the [Task] [Invocation] is a continuation of the currnent execution thread by providing it under `join` field. + +Often [Invocation] in the `join` field will synthesize [Result]s of the concurrent execution threads (spawned by `fork`), and incorporate them into the current execution thread. + +It is also possible for the thread to complete execution (omit `join` field) and leave concurrent threads behind. The `join` field, simply demarks the execution thread and provides a way to follow it through trail of [Receipt]s. + +## 7.1 Schema + +```ipldsch +# Represents a request to invoke enclosed set of tasks concurrently +type Effect { + # Primary set of tasks to be invoked + fork [&Invocation] (implicit "[]") + + # Additional task to be invoked with added semantics + # of representing a workflow execution continuation. + join optional &Invocation +} +``` + +## 7.2 Fields + +### 7.2.1 Forked Task Invocations + +The OPTIONAL `fork` field, if present MUST be a list of an alphabetically ordered [Invocation] links. List MUST NOT not contain duplicate entries. Every linked [Invocation] MUST have `cause` field set to the same [Invocation] (link) as the `run` field of the containing [Receipt]. + +### 7.2.2 Joined Task Invocation + +The OPTIONAL `join` field, if present MUST be set to an [Invocation] link. Linked [Invocation] in the `join` field MUST have `cause` field set to the same [Invocation] (link) as the `run` field of the containing [Receipt]. + +## 7.4 DAG-JSON Examples + +### 7.4.1 Effect spawning concurrent threads + +```json +{ + "fork": [ + { + "/": "bafyreigtbbrfic7gduybn2lcncbbzycbef3kt3aqeguko5dmuhpmf73zmu" + }, + { + "/": "bafyreidj2fkidh5g4tlldael7dfw27va6vbn6ah6pv7hirsandzwp25pj4" + } + ] +} +``` + +### 7.4.2 Effect continuing thread execution + +```json +{ + "join": { + "/": "bafyreigqsmtpw6qliojfzupzbxsnkn4yave7vov2jvvrtye7w4nomvd7yq" + } +} +``` + +### 7.4.1 Effect with fork & join -An [Invocation] Receipt is an attestation of the [Result] of an Invocation. A Receipt MUST be signed by the [Executor] or it's delegate. If signed by delegate, proof of delegation from [Executor] to the Issuer (the `iss` of the receipt) MUST be provided in `prf`. +```json +{ + "fork": [ + { + "/": "bafyreieljernknkcrmefokxxperobictfndo4v4cazkjmqfwuog4puawxm" + }, + { + "/": "bafyreiho3x2lbi6a5irw3orgrzrivkakr7iymalpukmxsvdsoqlxpcjsfq" + } + ], + "join": { + "/": "bafyreidtsxqd5fcuwdgverklwpcgbgzixsf6z6wgelybiakpwec3q75iwa" + } +} +``` + +# 8 Receipt + +A `Receipt` is an attestation of the [Result] and a caused [Effect] by the [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 Issuer (the `iss` of the receipt) MUST be provided in `prf`. **NB: a Receipt this 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. -## 7.1 Schema +## 8.1 Schema ```ipldsch -type Receipt struct { - ran &Invocation +type Receipt struct { + ran &Invocation # output of the invocation - out Result + out Result # Effects to be performed - fx optional &Promise - - # Related receipts - origin optional &Receipt + fx Effect (implicit "{}") # All the other metadata - meta { String: Any } (implicit {}) + meta {String: any} (implicit "{}") # Principal that issued this receipt. If omitted issuer is # inferred from the invocation task audience. @@ -911,157 +1088,149 @@ type Receipt struct { # When issuer is different from executor this MUST hold a UCAN # delegation chain from executor to the issuer. Should be omitted executor is an issuer. - prf [&UCAN] implicit ([]) + prf [&UCAN] (implicit "[]") # Signature from the `iss`. s Varsig } ``` -## 7.2 Fields +## 8.2 Fields -### 7.2.1 Ran Invocation +### 8.2.1 Ran Invocation The `ran` field MUST include a link to the [Invocation] that the Receipt is for. -### 7.2.2 Output +### 8.2.2 Output The `out` field MUST contain the output of the invocation in [Result] format. -### 7.2.3 Effects - -Some [Task]s may represent step in a complex workflow with multiple, sometimes concurrent, steps. Such [Task]s MAY capture state of the workflow in the `out` field of the [Receipt] and specify consequent step(s) using [Promise] in the `fx` field of the [Receipt]. - -The OPTIONAL `fx` field, if present MUST contain link to a [Promise]. [Executor] SHOULD perform the [Task] underlying a [Promise] in the `fx` field to progress a workflow execution. +### 8.2.3 Effect -If `fx` field is omitted, it denote an completion of the workflow execution. +The OPTIONAL `fx` field, if present MUST be set to the caused [Effect]. The [Executor] SHOULD perform contained [Task] [Invocation]s to progress a workflow execution. -### 7.2.4 Linked Receipts +If `fx` does not contain OPTIONAL `join` field, it denotes completion of the current execution thread. -Some [Invocation]s may represent step in a complex workflow with multiple steps. Each completed step in such a workflow would have corresponding [Receipt]. Each subsequent receipt SHOULD set `origin` field to a [Receipt] link of the preeceding step. This establishes a signed chain of receipts that leading to any one Receipt. - -The `origin` field MUST be omitted for [Invocation]s that represent first or only step in the workflow. - -### 6.2.4 Metadata Fields +### 8.2.4 Metadata Fields The 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. -### 6.2.5 Receipt Issuer +### 8.2.5 Receipt Issuer The OPTIONAL `iss` field, if present MUST contain the signer of the receipt. It MUST be an [Executor] or it's delegate. If delegate proof of delegation MUST be provided in `prf` field. If `iss` field is omitted, it MUST implicitly imply an [Executor]. -### 6.2.6 Proofs - +### 8.2.6 Proofs The `prf` field MUST contain links to UCAN(s) that that delegate authority to perform the invocation from the [Executor] to the Receipt issuer (`iss`). If [Executor] and the Issuer are same no proofs are required. -### 6.2.7 Signature +### 8.2.7 Signature The `s` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without `s` field. The signature MUST be generated by the issuer (`iss`). -## 6.3 DAG-JSON Examples +## 8.3 DAG-JSON Examples -### 6.3.1 Issued by Executor +### 8.3.1 Issued by Executor ```json { "ran": { - "/": "bafyreibt3t5uzjiwn4b3rszto6ox2z4najv45rzi75dooti3ad2rl7qkoi" + "/": "bafyreifkeu34pzda27fozfcboh25psekhqehkvnekltoiaspb5jsp6pj5q" }, "out": { "ok": { - "from": "bob@example.com", - "text": "Hello world!" + "members": [ + "bob@example.com", + "alice@web.mail" + ] } }, "meta": { "retries": 2, - "time": [400, "hours"] + "time": [ + 400, + "hours" + ] }, "s": { "/": { - "bytes": "7aEDQJBRBQPMyopsMw84vBQa2EdrRZtz9Kk7oF+tZ5eVIKSjeQwy1ReCGVXkt56cEhlOER7uOpIugSwUtf8VapW1TQM" + "bytes": "7aEDQIeyhI1chc8p3jpODozaxomV3y+rym9f+G7kIxyjsV0bqZZELGeCaYhIMYSmXk5aSAdufXJwLCFT6vsbm7DDqA0" } } } ``` -### 6.3.2 Issued by Delegate +### 8.3.2 Issued by Delegate ```json { "ran": { - "/": "bafyreicewv7jbynidkzljwc3okytusrljgjpn7xjamrrcnokgp5qei77gy" + "/": "bafyreifkeu34pzda27fozfcboh25psekhqehkvnekltoiaspb5jsp6pj5q" }, - "iss": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", "out": { "ok": { - "from": "bob@example.com", - "text": "Hello world!" + "members": [ + "bob@example.com", + "alice@web.mail" + ] } }, "meta": { "retries": 2, - "time": [400, "hours"] + "time": [ + 400, + "hours" + ] }, + "iss": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", "prf": [ - { "/": "bafyreihfgvlol74ugosa5gkzvbsghmq7wiqn4xvgack4uwn4qagrml6p74" } - ], - "s": { - "/": { - "bytes": "7aEDQBAv3B11GOo5Yoa/wMILlK0L3565ainer90OFa0h8XA4awZFKB3bfq0DL00HH+r4tKVq3k440HIG0apYcWppFwI" + { + "/": "bafyreihfgvlol74ugosa5gkzvbsghmq7wiqn4xvgack4uwn4qagrml6p74" } - } -} -``` - -### 6.3.3 Receipts with effects - -```json -{ - "ran": { - "/": "bafyreicrhbr7jjt6pvhldnrl7g6tr4r5uspgea52xo23a2yegfnpspeste" - }, - "out": { - "ok": {} - }, - "fx": { - "/": "bafyreib6mgm5few6pnajc25h6r6trp2kbi6xrmhafnmby3hrflmgql2kna" - }, + ], "s": { "/": { - "bytes": "7aEDQMO5k6OtYgHSRVIR2sqn97TTfrhLrdTSusoYOAaCV2lg37xI22QMjMhW44eVgkIRcWcbLO4YdrJfcwG4t3jzegM" + "bytes": "7aEDQB1URqWf6LhVUhbSgCOERp5wEGH0sEPqGvLrYpwsh5QslqVrwW8EXMjlRgeKeyHkM7yXxqzTXdnkB0tqFgMYOAk" } } } ``` -### 6.3.4 Chained Receipt +### 7.3.3 Receipts with effects ```json { "ran": { - "/": "bafyreicrhbr7jjt6pvhldnrl7g6tr4r5uspgea52xo23a2yegfnpspeste" + "/": "bafyreicjvdt44rclleycshdpkpsnxpksztn5bmx6w6ximjmod7osuvs76i" }, "out": { "ok": { - "capacity": "3GiB" + "status": 200 } }, - "origin": { - "/": "bafyreiga5grjokrjgjn62kbwdf6oz3w5m4hj4ck3ln6k6bw3wguiv2s6oy" + "fx": { + "fork": [ + { + "/": "bafyreieljernknkcrmefokxxperobictfndo4v4cazkjmqfwuog4puawxm" + }, + { + "/": "bafyreiho3x2lbi6a5irw3orgrzrivkakr7iymalpukmxsvdsoqlxpcjsfq" + } + ], + "join": { + "/": "bafyreidtsxqd5fcuwdgverklwpcgbgzixsf6z6wgelybiakpwec3q75iwa" + } }, "s": { "/": { - "bytes": "7aEDQFhBCM0NkHNMpHVn1UWnI90S0hHKMiDbqj4Gf3U3QNRpVM5/Ta9Uy94khq14TREmEWBwkqVMEk/EJ46a4NF33AY" + "bytes": "7aEDQMhFF0SuvqImvMRUAORuY5uJttauHMRvt0kwFRXkidPqvkrouDr49lBkMKrGrCcJslpcp+/MeG0FoTjxdo3KQQc" } } } ``` -# 8 Pipelines +# 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 > @@ -1071,59 +1240,104 @@ There MAY not be enough information to described an Invocation at creation time. 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. -A [Promise] / [Await] MAY be used as a variable placeholder for a concrete value in an [Invocation] output, waiting on a previous step to complete. +An [Await] MAY be used as a variable placeholder for a concrete value in an [Invocation] output, waiting on a previous step to complete. For example, consider the following invocation batch: ```json { - "bafyreif7wiz4mz3ojmmvrz2p5m6ay5nqbi7ghwmheccv5rqgjtkrk5wyi4": { - "v": "0.1.0", - "with": "https://example.com/blog/posts", - "do": "crud/create", - "input": { - "payload": { - "body": "This is the story of how one spec changed everything...", - "title": "How UCAN Tasks Changed My Life" + "blocks": { + "bafyreicxndtwegag3mmc6vos7xponkb2pkm7x5p5clbbgbheyuxssam6ka": { + "do": "crud/create", + "with": "https://example.com/blog/posts", + "input": { + "payload": { + "title": "How UCAN Tasks Changed My Life", + "body": "This is the story of how one spec changed everything..." + } } }, - "prf": [ - { "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" } - ] - }, - "bafyreifc2ytuc5dv454a7c6cxk3mm7gt6skoptzj5rtc3ijf65v2bjxssy": { - "v": "0.1.0", - "with": "https://example.com/users/editors", - "do": "crud/read", - "prf": [ - { "/": "bafyreie3ukg4h2kf7lnx7k62kjujlo2a5l66rh7e7vlj52fnsrj7tuc2ya" } - ] - }, - "bafyreibodwqz5ghsvf6nmopdbkksp46roc6zakmmd7gv6flymc2edeeq3q": { - "v": "0.1.0", - "with": "mailto:akiko@example.com", - "do": "msg/send", - "input": { - "to": { - "await/ok": { - "ucan/task": { - "/": "bafyreifc2ytuc5dv454a7c6cxk3mm7gt6skoptzj5rtc3ijf65v2bjxssy" - } + "bafyreici7ms2diig6y3ju64f3y5q3rn4zezbmxoiv3hwxleaoia6sg5v2u": { + "v": "0.1.0", + "run": { + "/": "bafyreicxndtwegag3mmc6vos7xponkb2pkm7x5p5clbbgbheyuxssam6ka" + }, + "prf": [ + { + "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" } + ] + }, + "bafyreibsgkiansdaty6eeuwtutmx6ztyp2wcmucoisyxzdlfeo74rhvuvi": { + "do": "crud/read", + "with": "https://example.com/users/editors" + }, + "bafyreiamphjpf5bwht6le4v2fsbxa55omerv3lpoyhi3bzzimcrii7vfri": { + "v": "0.1.0", + "run": { + "/": "bafyreibsgkiansdaty6eeuwtutmx6ztyp2wcmucoisyxzdlfeo74rhvuvi" }, - "subject": "Coffee", - "body": { - "await/ok": { - "ucan/task": { - "/": "bafyreif7wiz4mz3ojmmvrz2p5m6ay5nqbi7ghwmheccv5rqgjtkrk5wyi4" + "prf": [ + { + "/": "bafyreie3ukg4h2kf7lnx7k62kjujlo2a5l66rh7e7vlj52fnsrj7tuc2ya" + } + ] + }, + "bafyreiedbqqzfbo7kqdu3gjfcmfq7ffhzbs37qmu4q73mgtwm7h54jfbsm": { + "do": "msg/send", + "with": "mailto:akiko@example.com", + "input": { + "to": { + "await/ok": { + "/": "bafyreici7ms2diig6y3ju64f3y5q3rn4zezbmxoiv3hwxleaoia6sg5v2u" + } + }, + "subject": "Coffee", + "body": { + "await/ok": { + "/": "bafyreiamphjpf5bwht6le4v2fsbxa55omerv3lpoyhi3bzzimcrii7vfri" } } } }, - "prf": [ - { "/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4" } - ] - } + "bafyreic7ypuk4xd4kvintknoxfgy33ttkr3kv2vp2fenpylecofhuswiye": { + "v": "0.1.0", + "run": { + "/": "bafyreiedbqqzfbo7kqdu3gjfcmfq7ffhzbs37qmu4q73mgtwm7h54jfbsm" + }, + "auth": { + "/": "bafyreifz5u3yzkophibkf4w23doou4pvhmqdje27cwihnl7obsbyeo3qym" + }, + "prf": [ + { + "/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4" + } + ] + }, + "bafyreifz5u3yzkophibkf4w23doou4pvhmqdje27cwihnl7obsbyeo3qym": { + "scope": [ + { + "/": "bafyreiedbqqzfbo7kqdu3gjfcmfq7ffhzbs37qmu4q73mgtwm7h54jfbsm" + }, + { + "/": "bafyreicxndtwegag3mmc6vos7xponkb2pkm7x5p5clbbgbheyuxssam6ka" + }, + { + "/": "bafyreibsgkiansdaty6eeuwtutmx6ztyp2wcmucoisyxzdlfeo74rhvuvi" + } + ], + "s": { + "/": { + "bytes": "7aEDQJb+yWxXmprXwpBSvvMtyGjG75QHA90UkQduagjS1DpQnqE4NB3Bm41j7q8ODIKBnPXtZAHBaX/VxGUJvD8DXAo" + } + } + } + }, + "roots": [ + { + "/": "bafyreic7ypuk4xd4kvintknoxfgy33ttkr3kv2vp2fenpylecofhuswiye" + } + ] } ``` @@ -1152,84 +1366,83 @@ After resolution, the [Invocation] MUST be validated against the [Authorization] Promises 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). +## 9.2 Promise -## 8.1 Promise - -A `Promise` describes eventual output `Result` of the [Invocation]. +A `Promise` has a same representation as [Invocation] with only difference being that `auth` field is OPTIONAL. Promise MAY be used by an [Invocation] to [Await] another [Invocation] authorized with the same [Authorization]. [Invocation] MAY be used directly everywhere else in place of the [Promise]. -### 8.1.1 Schema +### 9.2.1 Schema -```ipldsch -type Promise union { - | &Invocation ("ucan/invocation") - | &Task ("ucan/task") -} representation keyed -``` - -#### 8.1.2 Variants +```ipldsh +# Promise is an Invocation with optional 'auth' field which if omitted +# is implicitly an 'auth' of the Invocation that contains Await. +type Promise struct { + v SemVer -##### 8.1.2.1 Invocation Promise + run &Task + # Receipt of the invocation that caused this invocation + cause optional &Invocation + # Task authorization. If omitted can be interpreted as requires + # authorization + auth optional &Authorization -A `Promise` of the [Invocation] MUST be a link to that invocation wrapped in the `"ucan/invocation"` tag. + meta {String : any} (implicit "{}") -```ts -{ - "ucan/invocation": { - "/": "bafyreihdw5dpnggixaee3rwn5kr3vhvfvokj3hi24uk2swp4nizyjdhdeq" - } + prf [&UCAN] (implicit "[]") } -``` -##### 8.1.2.2 Task Promise +``` -An `Promise` of the [Invocation] that shares [Authorization] with a [Task] referencing it MUST be a link to the invocation [Task] wrapped in the `"ucan/task"` tag. +## 9.3 Await -Note that [Task]s with the same [Authorization] will be unable to reference [Invocation] since [Authorization] CAN be created after all the [Task]s being authorized. The `"ucan/task"` tagged Promise MAY be used in such cases. +An `Await` describes an output of the [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. -## 8.2 Await +An [Invocation] from the [Await] is resolved by: -An `Await` describes an output of the [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. +1. Creating a new [Invocation]. +1. Setting all of the fields, but `auth` to same values as an underlying [Promise]. +1. If OPTIONAL `auth` field of the underlying [Promise] is set: + - then set `auth` of the invoaction to the same value + - else set `auth` of the invocation to the `auth` of the [Invocation] containing [Await] been resolved. -### 8.2.1 Schema +### 9.3.1 Schema ```ipldsch -type Await union { - &Promise "await/*" - &Promise "await/ok" - &Promise "await/error" +type Await union { + | &Promise "await/*" + | &Promise "await/ok" + | &Promise "await/error" } representation keyed ``` -#### 8.2.2 Variants +#### 9.3.2 Variants -##### 8.2.2.1 Success +##### 9.3.2.1 Success -The successful output of the [Invocation] MAY be referenced by wrapping a [Promise] in the `"await/ok"` tag. +The successful output of the [Invocation] MAY be referenced by wrapping corresponding [Promise] in the `"await/ok"` tag. [Executor] MUST fail [Invocation] that `Await`s successful output of the failed [Invocation]. [Executor] MUST substitute [Task] field set to the [Await] of the successful [Invocation] with an (unwrapped) `ok` value of the output. -##### 8.2.2.1 Failure +##### 9.3.2.1 Failure -The failed output of the [Invocation] MAY be referenced by wrapping a [Promise] in the `"await/error"` tag. +The failed output of the [Invocation] MAY be referenced by wrapping corresponding [Promise] in the `"await/error"` tag. [Executor] MUST fail [Invocation] that `Await`s failed output of the successful [Invocation]. [Executor] MUST substitute [Task] field set to the [Await] of the failed [Invocation] with an (unwrapped) `error` value of the output. -##### 8.2.2.1 Result +##### 9.3.2.1 Result -The [Result] output of the [Invocation] MAY be reference by wrapping a [Promise] in the `"await/*"` tag. +The [Result] output of the [Invocation] MAY be reference by wrapping corresponding [Promise] in the `"await/*"` tag. [Executor] MUST substitute [Task] field set to the [Await] of the [Invocation] with a `Result` value of the output. - -## 8.3 Dataflow +## 9.4 Dataflow Pipelining uses [Await] as inputs to determine the required dataflow graph. The following examples both express the following dataflow graph: -### 7.3.1 Batched +### 9.4.1 Batched ```mermaid flowchart BR @@ -1265,7 +1478,7 @@ flowchart BR }, "prf": [ { "/": "bafyreieynwqrabzdhgl652ftsk4mlphcj3bxchkj2aw5eb6dc2wxieilau" } - ], + ] }, "bafyreieeipburgtzg4hr2ghxgspc53szrkstaz4syjat3tex75hjdrau3y": [ { "/": "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm" }, @@ -1325,10 +1538,7 @@ flowchart BR "payload": { "event": "email-notification", "from": "mailto://alice@exmaple.com", - "to": [ - "bob@exmaple.com", - "carol@example.com" - ] + "to": ["bob@exmaple.com", "carol@example.com"] }, "_": [ { @@ -1345,7 +1555,7 @@ flowchart BR } } } - ], + ] }, "prf": [ { "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" } @@ -1374,8 +1584,7 @@ flowchart BR } ``` - -### 7.3.2 Serial +### 9.4.2 Serial ```mermaid flowchart TB @@ -1410,59 +1619,71 @@ flowchart TB ```json { "blocks": { - "bafyreid5ltmhwuhgcbxa3dwd5erymqdpfsyhlwudlfylu3wrzy6fjbvuoy": [ - { "/": "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli" }, - { "/": "bafyreicwrzsyonu4efnwtmwqax2s2fbv5padbyk66zwl6kkvrkrd5qavpy" } - ], - "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli": { - "v": "0.1.0", - "with": "dns:example.com?TYPE=TXT", + "bafyreifcerdvicarktlnif5uj25ultgpedwg63nxhmp7anoepaamqj4eji": { "do": "crud/update", + "with": "dns:example.com?TYPE=TXT", "input": { "value": "hello world" + } + }, + "bafyreieogoqh5rolyt4livorznwhg63ml4oon3vdxnkqvc7svya4gisppe": { + "v": "0.1.0", + "run": { + "/": "bafyreifcerdvicarktlnif5uj25ultgpedwg63nxhmp7anoepaamqj4eji" }, "prf": [ - { "/": "bafyreieynwqrabzdhgl652ftsk4mlphcj3bxchkj2aw5eb6dc2wxieilau" } + { + "/": "bafyreieynwqrabzdhgl652ftsk4mlphcj3bxchkj2aw5eb6dc2wxieilau" + } ] }, - "bafyreigbtlydjneybqpy6r3u5hobsempr335tjw4ozm2aun5yrcj2whdgi": [ - { "/": "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm" }, - { "/": "bafyreicwrzsyonu4efnwtmwqax2s2fbv5padbyk66zwl6kkvrkrd5qavpy" } - ], - "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm": { - "v": "0.1.0", - "with": "mailto://alice@example.com", + "bafyreieqqhr7pznb77ffngpqnhmlcoi3666zc7qmhvdzs26oy6fclqp4dy": { "do": "msg/send", + "with": "mailto://alice@example.com", "input": { + "to": "bob@example.com", + "subject": "DNSLink for example.com", "body": { "await/ok": { - "ucan/task": { - "/": "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli" - } + "/": "bafyreieogoqh5rolyt4livorznwhg63ml4oon3vdxnkqvc7svya4gisppe" } - }, - "subject": "DNSLink for example.com", - "to": "bob@example.com" + } + } + }, + "bafyreid52dldaayikwgscn43o5gurlrujmlppdejductzdqnsvizvjplze": { + "v": "0.1.0", + "run": { + "/": "bafyreieqqhr7pznb77ffngpqnhmlcoi3666zc7qmhvdzs26oy6fclqp4dy" + }, + "auth": { + "/": "bafyreifxyud6cnw26g3t54xsjcjilu6j7laqlge6fds6rgcezl2waivvdi" }, "prf": [ - { "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" } + { + "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" + } ] }, - "bafyreicwrzsyonu4efnwtmwqax2s2fbv5padbyk66zwl6kkvrkrd5qavpy": { + "bafyreifxyud6cnw26g3t54xsjcjilu6j7laqlge6fds6rgcezl2waivvdi": { "scope": [ - { "/": "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli" }, - { "/": "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm" } + { + "/": "bafyreifcerdvicarktlnif5uj25ultgpedwg63nxhmp7anoepaamqj4eji" + }, + { + "/": "bafyreieqqhr7pznb77ffngpqnhmlcoi3666zc7qmhvdzs26oy6fclqp4dy" + } ], "s": { "/": { - "bytes": "7aEDQKrLlk87jEJh2ftRMXidqypRf2QtwFUvcTWvKN9G1D5XeLB8o6TtPv2qTF/b+s9r6DAQAavilk8J10yFYDyMUAA" + "bytes": "7aEDQFftPMepLGONVlthmxHaa7UbPnwVwJnDdIPrV/WcBTb1lot7OrEblJShcnrHrtcDNe8CRXQK9N58fPjYt4OQfQM" } } } }, "roots": [ - { "/": "bafyreid5ltmhwuhgcbxa3dwd5erymqdpfsyhlwudlfylu3wrzy6fjbvuoy" }, - { "/": "bafyreigbtlydjneybqpy6r3u5hobsempr335tjw4ozm2aun5yrcj2whdgi" } + { + "/": "bafyreid52dldaayikwgscn43o5gurlrujmlppdejductzdqnsvizvjplze" + } ] } ``` @@ -1470,84 +1691,95 @@ flowchart TB ```json { "blocks": { - "bafyreif4zur3xni5fal23ybxjsw4bm5ezfjp6xgfwjd5ndextr55kr4i34": [ - { "/": "bafyreigl5x2p3ehppg7xbwexdsr63ljfkcr4oj76lwacwj4rt5vfs2cvky" }, - { "/": "bafyreiacpop3w22qc722sdovynbwi2so6r2xetqx5hf4xz326iu3we5sqa" } - ], - "bafyreigl5x2p3ehppg7xbwexdsr63ljfkcr4oj76lwacwj4rt5vfs2cvky": { - "v": "0.1.0", - "with": "mailto://alice@example.com", + "bafyreif2l5ykv6pbdxd636o2iyhr7evorygjczhbfrlhkr5t65t5xjjdm4": { "do": "msg/send", + "with": "mailto://alice@example.com", "input": { "to": "carol@example.com", "subject": "Hey Carol, DNSLink was updated!", "body": { "await/ok": { - "ucan/invocation": { - "/": "bafyreid5ltmhwuhgcbxa3dwd5erymqdpfsyhlwudlfylu3wrzy6fjbvuoy" - } + "/": "bafyreieogoqh5rolyt4livorznwhg63ml4oon3vdxnkqvc7svya4gisppe" } } + } + }, + "bafyreifwe7q42d54pxmdszzdcayagrkb4btsto5zcfwn434n7yq57v3mxe": { + "v": "0.1.0", + "run": { + "/": "bafyreif2l5ykv6pbdxd636o2iyhr7evorygjczhbfrlhkr5t65t5xjjdm4" }, "prf": [ - { "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" } + { + "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" + } ] }, - "bafyreibvwhdqydz4qq3narocqsvnqbxoegxgmmlp35mewn53rjxeltfccu": [ - { "/": "bafyreifaip4infpqh76r7nlscubkfznhoiko224i2sthy7w3atfsmrff2a" }, - { "/": "bafyreiacpop3w22qc722sdovynbwi2so6r2xetqx5hf4xz326iu3we5sqa" } - ], - "bafyreifaip4infpqh76r7nlscubkfznhoiko224i2sthy7w3atfsmrff2a": { - "v": "0.1.0", - "with": "https://example.com/report", + "bafyreieym67mo7s5wjgwcnxrnn75vn4nn5w33snicuc34ppp4t5dshphya": { "do": "crud/update", + "with": "https://example.com/report", "input": { "payload": { "from": "mailto://alice@exmaple.com", - "to": ["bob@exmaple.com", "carol@example.com"], + "to": [ + "bob@exmaple.com", + "carol@example.com" + ], "event": "email-notification" }, "_": [ { "await/ok": { - "ucan/invocation": { - "/": "bafyreigbtlydjneybqpy6r3u5hobsempr335tjw4ozm2aun5yrcj2whdgi" - } + "/": "bafyreid52dldaayikwgscn43o5gurlrujmlppdejductzdqnsvizvjplze" } }, { "await/ok": { - "ucan/task": { - "/": "bafyreifjjvdd6hoi7wjot7tjaubwztqldds332bgq6a4n37d6s5slxkbvy" - } + "/": "bafyreifwe7q42d54pxmdszzdcayagrkb4btsto5zcfwn434n7yq57v3mxe" } } ] + } + }, + "bafyreiahe6v4vsr3gzqn6s6wxcuauyoqvuzh524tkxiwr4lzcskywsc6p4": { + "v": "0.1.0", + "run": { + "/": "bafyreieym67mo7s5wjgwcnxrnn75vn4nn5w33snicuc34ppp4t5dshphya" + }, + "auth": { + "/": "bafyreih6b5uc3xzhh6xnta7iqbwhe5t62kekzcjpssx5adyh5pky7m5j2e" }, "prf": [ - { "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" } + { + "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" + } ] }, - "bafyreiacpop3w22qc722sdovynbwi2so6r2xetqx5hf4xz326iu3we5sqa": { + "bafyreih6b5uc3xzhh6xnta7iqbwhe5t62kekzcjpssx5adyh5pky7m5j2e": { "scope": [ - { "/": "bafyreigl5x2p3ehppg7xbwexdsr63ljfkcr4oj76lwacwj4rt5vfs2cvky" }, - { "/": "bafyreifaip4infpqh76r7nlscubkfznhoiko224i2sthy7w3atfsmrff2a" } + { + "/": "bafyreif2l5ykv6pbdxd636o2iyhr7evorygjczhbfrlhkr5t65t5xjjdm4" + }, + { + "/": "bafyreieym67mo7s5wjgwcnxrnn75vn4nn5w33snicuc34ppp4t5dshphya" + } ], "s": { "/": { - "bytes": "7aEDQBCcyIzfbMRT/UnUF3LgY7Xp/hhhpJfr4kRuEQEgmxcATSE1iLD7VOLMA0nauhqRM7RFUIGjxt1CAf4tZY+yOQE" + "bytes": "7aEDQKkxnSFpFpj51Q715C7FWemiXgSKXdHZyN0XBsOym+/yBaF46iX98XYF789cdkRBQACUfzM5cRhFYvwZpljitA8" } } } }, "roots": [ - { "/": "bafyreif4zur3xni5fal23ybxjsw4bm5ezfjp6xgfwjd5ndextr55kr4i34" }, - { "/": "bafyreibvwhdqydz4qq3narocqsvnqbxoegxgmmlp35mewn53rjxeltfccu" } + { + "/": "bafyreiahe6v4vsr3gzqn6s6wxcuauyoqvuzh524tkxiwr4lzcskywsc6p4" + } ] } ``` -# 6 Prior Art +# 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. @@ -1559,7 +1791,7 @@ The [Object Capability Network (OCapN)](https://github.com/ocapn/ocapn) protocol [Cap 'n Proto RPC](https://capnproto.org/) is an influential RPC framework [based on concepts from CapTP](https://capnproto.org/rpc.html#specification). -# 12 Acknowledgements +# 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/). @@ -1579,24 +1811,21 @@ 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/ -[ipld schema]: https://ipld.io/docs/schemas/ [ucan-ipld]: https://github.com/ucan-wg/ucan-ipld/ [ucan]: https://github.com/ucan-wg/spec/ [dag-cbor]: https://ipld.io/specs/codecs/dag-cbor/spec/ -[car]: https://ipld.io/specs/transport/car/carv1/ -[ipld representation]:https://ipld.io/docs/schemas/features/representation-strategies/ -[lazy-vs-eager]: #112-Lazy-vs-Eager-Evaluation +[ipld representation]: https://ipld.io/docs/schemas/features/representation-strategies/ +[lazy-vs-eager]: #112-lazy-vs-eager-evaluation [invoker]: #211-invoker [executor]: #212-executor [task]: #3-task [authorization]: #4-authorization -[invocation]: #5-Invocation -[result]: #6-Result -[receipt]: #7-receipt -[pipelines]: #8-Pipelines -[promise]: #81-promise -[await]: #82-await -[dataflow]: #83-dataflow +[invocation]: #5-invocation +[result]: #6-result +[effect]: #7-effect +[receipt]: #8-receipt +[pipelines]: #9-pipelines +[promise]: #92-promise +[await]: #93-await From 3d3f29260919ed9aba6a4f6d6773981e4a2603ca Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Mon, 6 Feb 2023 22:20:01 -0800 Subject: [PATCH 136/154] reconfigure lint --- .github/workflows/linkcheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linkcheck.yml b/.github/workflows/linkcheck.yml index d3833842..10fa5324 100644 --- a/.github/workflows/linkcheck.yml +++ b/.github/workflows/linkcheck.yml @@ -19,4 +19,4 @@ jobs: config-file: "./.github/workflows/linkcheck.cfg.json" - uses: DavidAnson/markdownlint-cli2-action@v9 with: - globs: "*.md" + globs: "README.md" From c42a2fc997b144cd282531348ac576a9385c8cb8 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Mon, 6 Feb 2023 22:28:24 -0800 Subject: [PATCH 137/154] fix: spelling errors --- .github/workflows/spellcheck.yml | 6 ++++-- README.md | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index 50a25347..b96302bf 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -1,7 +1,9 @@ -name: 'spellcheck' +name: "spellcheck" on: - pull_request: push: + branches: + - main + pull_request: jobs: spellcheck: diff --git a/README.md b/README.md index 3b0ea2d7..e9c62226 100644 --- a/README.md +++ b/README.md @@ -989,7 +989,7 @@ Effect MAY instruct the [Executor] that the [Task] [Invocation] is a continuatio Often [Invocation] in the `join` field will synthesize [Result]s of the concurrent execution threads (spawned by `fork`), and incorporate them into the current execution thread. -It is also possible for the thread to complete execution (omit `join` field) and leave concurrent threads behind. The `join` field, simply demarks the execution thread and provides a way to follow it through trail of [Receipt]s. +It is also possible for the thread to complete execution (omit `join` field) and leave concurrent threads behind. The `join` field, simply marks the execution thread and provides a way to follow it through trail of [Receipt]s. ## 7.1 Schema @@ -1401,7 +1401,7 @@ An [Invocation] from the [Await] is resolved by: 1. Creating a new [Invocation]. 1. Setting all of the fields, but `auth` to same values as an underlying [Promise]. 1. If OPTIONAL `auth` field of the underlying [Promise] is set: - - then set `auth` of the invoaction to the same value + - then set `auth` of the invocation to the same value - else set `auth` of the invocation to the `auth` of the [Invocation] containing [Await] been resolved. ### 9.3.1 Schema From d5fbbc16733afad668a9f94af0e98530c4120a45 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Mon, 6 Feb 2023 22:29:55 -0800 Subject: [PATCH 138/154] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e9c62226..57ef1187 100644 --- a/README.md +++ b/README.md @@ -985,7 +985,7 @@ All of the [Invocation]s of the `Effect` MUST be concurrent with one another, un Effect MAY instruct the [Executor] to run set of [Task] [Invocation]s in concurrent execution threads by providing them as list under `fork` field. -Effect MAY instruct the [Executor] that the [Task] [Invocation] is a continuation of the currnent execution thread by providing it under `join` field. +Effect MAY instruct the [Executor] that the [Task] [Invocation] is a continuation of the current execution thread by providing it under `join` field. Often [Invocation] in the `join` field will synthesize [Result]s of the concurrent execution threads (spawned by `fork`), and incorporate them into the current execution thread. From cea47ba95041fc40e897fb117ec6b6f90fb54212 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Mon, 6 Feb 2023 22:45:42 -0800 Subject: [PATCH 139/154] remove implicits as they are obsolete https://github.com/ipld/ipld/issues/268#issuecomment-1420256460 --- README.md | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 57ef1187..0ab5c83f 100644 --- a/README.md +++ b/README.md @@ -374,9 +374,9 @@ An [Effect] are the instruction to the [Executor] to run set of [Task]s concurre type Task struct { with URI do Ability - input {String: any} (implicit "{}") + input {String: any} - nnc string (implicit "''") + nnc string } type URI string @@ -384,7 +384,7 @@ type Ability string 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 } @@ -398,9 +398,9 @@ type Invocation struct { # Task authorization. auth &Authorization - meta {String : any} (implicit "{}") + meta {String : any} - prf [&UCAN] (implicit "[]") + prf [&UCAN] } type SemVer string @@ -412,18 +412,19 @@ type Receipt struct { out Result # Effects to be performed - fx Effect (implicit "{}") + fx Effect # All the other metadata - meta {String: any} (implicit "{}") + meta {String: any} # 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. Should be omitted executor is an issuer. - prf [&UCAN] (implicit "[]") + # delegation chain from executor to the issuer. Should be omitted when + # executor is an issuer. + prf [&UCAN] # Signature from the "iss". s Varsig @@ -437,7 +438,7 @@ type Result union { # Represents a request to invoke enclosed set of tasks concurrently type Effect struct { # Primary set of tasks to be invoked - fork [&Invocation] (implicit "[]") + fork [&Invocation] # Additional task to be invoked with added semantics # of representing a workflow execution continuation. @@ -456,9 +457,9 @@ type Promise struct { # authorization auth optional &Authorization - meta {String : any} (implicit "{}") + meta {String : any} - prf [&UCAN] (implicit "[]") + prf [&UCAN] } # Promise is a way to reference result of the invocation @@ -538,17 +539,16 @@ const sendEmail = msg.send("mailto://alice@example.com", { ```ipldsch type Task struct { - v SemVer with URI do Ability - input {String:any} (implicit "{}") - meta {String:any} (implicit "{}") - nnc string (implicit "''") - prf [&UCAN] (implicit "[]") + input {String: any} + + nnc string } + type URI string -type SemVer string +type Ability string ``` @@ -717,9 +717,9 @@ type Invocation struct { # Task authorization. auth &Authorization - meta {String : any} (implicit "{}") + meta {String : any} - prf [&UCAN] (implicit "[]") + prf [&UCAN] } type SemVer string ``` @@ -997,7 +997,7 @@ It is also possible for the thread to complete execution (omit `join` field) and # Represents a request to invoke enclosed set of tasks concurrently type Effect { # Primary set of tasks to be invoked - fork [&Invocation] (implicit "[]") + fork [&Invocation] # Additional task to be invoked with added semantics # of representing a workflow execution continuation. @@ -1077,10 +1077,10 @@ type Receipt struct { # output of the invocation out Result # Effects to be performed - fx Effect (implicit "{}") + fx Effect # All the other metadata - meta {String: any} (implicit "{}") + meta {String: any} # Principal that issued this receipt. If omitted issuer is # inferred from the invocation task audience. @@ -1088,7 +1088,7 @@ type Receipt struct { # When issuer is different from executor this MUST hold a UCAN # delegation chain from executor to the issuer. Should be omitted executor is an issuer. - prf [&UCAN] (implicit "[]") + prf [&UCAN] # Signature from the `iss`. s Varsig @@ -1372,7 +1372,7 @@ A `Promise` has a same representation as [Invocation] with only difference being ### 9.2.1 Schema -```ipldsh +```ipldsch # Promise is an Invocation with optional 'auth' field which if omitted # is implicitly an 'auth' of the Invocation that contains Await. type Promise struct { @@ -1385,9 +1385,9 @@ type Promise struct { # authorization auth optional &Authorization - meta {String : any} (implicit "{}") + meta {String : any} - prf [&UCAN] (implicit "[]") + prf [&UCAN] } ``` From 4cc81111ba279e605415faf185d3596b5e834ffe Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 7 Feb 2023 19:17:30 -0800 Subject: [PATCH 140/154] Apply suggestions from code review Co-authored-by: Brooklyn Zelenka Signed-off-by: Irakli Gozalishvili --- .clabot | 3 ++- README.md | 27 ++++++++++++++++----------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/.clabot b/.clabot index 5d6b5d93..4ebdafdf 100644 --- a/.clabot +++ b/.clabot @@ -1,6 +1,7 @@ { "contributors": [ "actions-user", - "expede" + "expede", + "gozala" ] } diff --git a/README.md b/README.md index 0ab5c83f..cce13d68 100644 --- a/README.md +++ b/README.md @@ -63,9 +63,13 @@ 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 Gossiping of delegations +## 1.2 Delegation Gossip -In web3.storage user `alice@web.mail` can delegate to store file in her space to `bob@send.io` by sending that delegation to `web3.storage`. If service were to interpret this as invocation it would fail due to principal misalignment. By distinguishing capability invocation from delegation service is able to more correctly handle such a message, if it is an invocation it will still error due to principal misalignment, if it is a delegation it will hold it in Bob's inbox to be picked up when he's comes online. +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 benificial 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 this 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 @@ -398,7 +402,7 @@ type Invocation struct { # Task authorization. auth &Authorization - meta {String : any} + meta {String : Any} prf [&UCAN] } @@ -440,8 +444,7 @@ type Effect struct { # Primary set of tasks to be invoked fork [&Invocation] - # Additional task to be invoked with added semantics - # of representing a workflow execution continuation. + # Continuation for straight-line programs join optional &Promise } @@ -703,7 +706,9 @@ The `s` field MUST contain a [Varsig] of the [CBOR] encoded `scope` field. 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]. -If `auth` field MUST be set to the [Authorization] (link) which contains `run` in it's scope field. The Invocation of the [Task] with an [Authorization] which does not includes [Task] in the `scope` MUST be considered invalid. +The `auth` field MUST be contain an [Authorization](#4-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. ## 5.1 Schema @@ -732,11 +737,11 @@ The `run` field MUST contain a link to the [Task] to be run. ### 5.2.2 Authorization -The `auth` field MUST contain a link to the [Authorization] that authorizes invoked [Task] in the `run` field. Linked [Authorization] MUST contain `run` in it's `scope`. +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.2.3 Cause -Some [Task]s may be invoked as an effect caused by another [Task] [Invocation]. Such Invocations SHOULD have `cause` field set to the [Invocation] (link) that caused it. The [Receipt] of the causing [Invocation] (Invocation linked from `cause` field) SHOULD have an `Effect` (the `fx` field) containing cased [Invocation]. +[Task]s MAY be invoked as an effect caused by another [Task] [Invocation]. These SHOULD have `cause` field set to the [Invocation] (link) that caused it. The [Receipt] of the causing [Invocation] (Invocation linked from `cause` field) MUST have an `Effect` (the `fx` field) containing cased [Invocation]. ## 5.3 DAG-JSON Example @@ -1103,7 +1108,7 @@ The `ran` field MUST include a link to the [Invocation] that the Receipt is for. ### 8.2.2 Output -The `out` field MUST contain the output of the invocation in [Result] format. +The `out` field MUST contain the value output of the invocation in [Result] format. ### 8.2.3 Effect @@ -1360,7 +1365,7 @@ const notify = msg.send("mailto:akiko@example.com", { }) ``` -While a [Await] MAY be substituted for any field in a [Task], substituting the `do` field is NOT RECOMMENDED. The `do` field is critical in understanding what kind of action will be performed, and schedulers SHOULD use this fields to grant atomicity, parallelize tasks, and so on. +While an [Await] MAY be substituted for any field in a [Task], substituting the `do` field is NOT RECOMMENDED. The `do` field is critical in understanding what kind of action will be performed, and schedulers SHOULD use this fields to grant atomicity, parallelize tasks, and so on. After resolution, the [Invocation] MUST be validated against the [Authorization] and linked UCAN proofs by the [Executor]. A Promise resolved to an [Invocation] that is not backed by a valid UCAN MUST NOT be executed, and SHOULD return an unauthorized error to the user. A Promise resolved to an [Invocation] with the [Authorization] that does not include invoked [Task] MUST NOT be executed, and SHOULD return an unauthorized error to the user. @@ -1394,7 +1399,7 @@ type Promise struct { ## 9.3 Await -An `Await` describes an output of the [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. +An `Await` describes the eventual output of the referenced [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. An [Invocation] from the [Await] is resolved by: From 2c6806b0434791a0c5ac277a83b617e0a1090217 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 7 Feb 2023 19:19:01 -0800 Subject: [PATCH 141/154] fix markup Signed-off-by: Irakli Gozalishvili --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cce13d68..024c7dca 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,7 @@ The JSON examples below are given in [DAG-JSON], but UCAN Task is actually defin ```json {"/": "Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD"} +``` ### Bytes From bf478ba063f2a4fe7b100230dbd627284a662939 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 7 Feb 2023 19:22:06 -0800 Subject: [PATCH 142/154] Apply suggestions from code review Signed-off-by: Irakli Gozalishvili --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 024c7dca..39ed14a5 100644 --- a/README.md +++ b/README.md @@ -446,7 +446,7 @@ type Effect struct { fork [&Invocation] # Continuation for straight-line programs - join optional &Promise + join optional &Invocation } # Promise is an Invocation with optional 'auth' field which if omitted From 5c56a94956810fe108133f876bbcf8579ff3c288 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 7 Feb 2023 19:23:40 -0800 Subject: [PATCH 143/154] Update README.md Signed-off-by: Irakli Gozalishvili --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 39ed14a5..d84ca36d 100644 --- a/README.md +++ b/README.md @@ -707,7 +707,7 @@ The `s` field MUST contain a [Varsig] of the [CBOR] encoded `scope` field. 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](#4-Authorization) which signs over the `&Task` in `run`. +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. From 8c1f7cd2b7a40e93da05d412bfdad103b2a679f0 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 8 Feb 2023 20:42:03 -0800 Subject: [PATCH 144/154] Apply suggestions from code review Co-authored-by: Brooklyn Zelenka Signed-off-by: Irakli Gozalishvili --- README.md | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index d84ca36d..9cd3a928 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ message() // A message interrupts the user Delegating a capability is like the statement `message`. Task is akin to `message()`. It's true that sometimes we know to run things from their surrounding context without the parentheses: ```js -;[1, 2, 3].map(message) // Message runs 3 times +[1, 2, 3].map(message) // Message runs 3 times ``` 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. @@ -355,11 +355,11 @@ A [Task] is like a deferred function application: a request to perform some acti ### 2.2.2 Authorization -An [Authorization] is a cryptographically signed proof permitting execution of referenced tasks. It allows invoker to authorize a group of tasks using one cryptographic signature. +An [Authorization] is a cryptographically signed proof permitting execution of referenced tasks. It allows the [Invoker] to authorize a group of tasks using one cryptographic signature. ### 2.2.3 Invocation -An [Invocation] is an invoker authorized instruction to the [Executor] to run the [Task]. +An [Invocation] is a command to the [Executor] to run the [Task], authorized by the [Invoker]. ### 2.2.4 Result @@ -379,7 +379,7 @@ An [Effect] are the instruction to the [Executor] to run set of [Task]s concurre type Task struct { with URI do Ability - input {String: any} + input {String : Any} nnc string } @@ -420,7 +420,8 @@ type Receipt struct { fx Effect # All the other metadata - meta {String: any} + # All the other metadata + meta {String : Any} # Principal that issued this receipt. If omitted issuer is # inferred from the invocation task audience. @@ -443,10 +444,10 @@ type Result union { # Represents a request to invoke enclosed set of tasks concurrently type Effect struct { # Primary set of tasks to be invoked - fork [&Invocation] + fork [&Promise] # Continuation for straight-line programs - join optional &Invocation + join optional &Promise } # Promise is an Invocation with optional 'auth' field which if omitted @@ -677,7 +678,7 @@ type Authorization struct { The `scope` field MUST be a set of links been authorized. It SHOULD be encoded as an alphabetically ordered list without duplicates. -If `scope` field is omitted, it is implicitly a an empty list and has no practical use as it authorizes nothing. +If the `scope` field is omitted, it is implicitly treated as an empty list (authorizing nothing). ### 4.2.2 Signature @@ -742,7 +743,7 @@ The `auth` field MUST contain a link to the [Authorization] that authorizes invo ### 5.2.3 Cause -[Task]s MAY be invoked as an effect caused by another [Task] [Invocation]. These SHOULD have `cause` field set to the [Invocation] (link) that caused it. The [Receipt] of the causing [Invocation] (Invocation linked from `cause` field) MUST have an `Effect` (the `fx` field) containing cased [Invocation]. +[Task]s MAY be invoked as an effect caused by a prior [Invocation]. Such [Invocation]s SHOULD have a link to the [Invocation] that caused it in the `cause` field. The [Receipt] of the causing [Invocation] (Invocation linked from `cause` field) MUST have an `Effect` (the `fx` field) containing caused [Invocation]. ## 5.3 DAG-JSON Example @@ -983,19 +984,15 @@ If no information is available, this field SHOULD be set to `{}`. } ``` -# 7 Effect - -Workflows often consist of many, sometimes concurrent, steps which form an execution threads. Steps of such workflows are discrete [Task] [Invocation]s, each producing some [Result] and a description of subsequent step(s) caused by it. Subsequent steps are denoted with an `Effect`, a set of [Invocation]s that [Executor] must perform to advance workflow execution. - -All of the [Invocation]s of the `Effect` MUST be concurrent with one another, unless explicitly arranged differently using [Pipelines]. +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 reuqesting impure tasks to be performed by the runtime, and gives the runtime the control to decide how (or if!) more work should be performed. -Effect MAY instruct the [Executor] to run set of [Task] [Invocation]s in concurrent execution threads by providing them as list under `fork` field. +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`. -Effect MAY instruct the [Executor] that the [Task] [Invocation] is a continuation of the current execution thread by providing it under `join` field. +[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 guarintees such as failure of one effect impling failure of other effects if left undefined. -Often [Invocation] in the `join` field will synthesize [Result]s of the concurrent execution threads (spawned by `fork`), and incorporate them into the current execution thread. +The `join` field describes an OPTIONAL "special" [Invocation] that is treated as a "main" thread. Effects MAY instruct the [Executor] that the [Task] [Invocation] is a continuation of the previous Invocation by providing it under `join` field. This roughly emulates a "main" virtual thread. -It is also possible for the thread to complete execution (omit `join` field) and leave concurrent threads behind. The `join` field, simply marks the execution thread and provides a way to follow it through trail of [Receipt]s. +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 @@ -1068,7 +1065,7 @@ The OPTIONAL `join` field, if present MUST be set to an [Invocation] link. Linke # 8 Receipt -A `Receipt` is an attestation of the [Result] and a caused [Effect] by the [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 Issuer (the `iss` of the receipt) MUST be provided in `prf`. +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 Issuer (the `iss` of the receipt) MUST be provided in `prf`. **NB: a Receipt this does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. From 978914fc9047735cc3f42455b83b9f16832b929f Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 8 Feb 2023 20:43:49 -0800 Subject: [PATCH 145/154] remove components diagram Signed-off-by: Irakli Gozalishvili --- README.md | 182 ------------------------------------------------------ 1 file changed, 182 deletions(-) diff --git a/README.md b/README.md index 9cd3a928..d3603071 100644 --- a/README.md +++ b/README.md @@ -167,188 +167,6 @@ The executor MUST be the UCAN delegate. Their DID MUST be set the in `aud` field ## 2.2 Components -```mermaid -stateDiagram-v2 -direction TB - -auth: Authorization - -send: Invocation -send.task: Task -send.promise: Await -read.promise: Await -send.receipt: Receipt -read: Invocation -read.task: Task - -update: Invocation -update.task: Task - -state auth { - direction LR - - s-->scope - -} - -state send.task { - direction LR - - send.task.prf: prf - send.task.do: do - send.task.with: with - send.task.input: input - send.task.nnc: nnc - send.task.meta: meta - - - send.do: msg/send - send.with: mailto꞉//alice@example.com - send.input: {"to"꞉ "bob@example.com", "subject"꞉ "DNSLink for example.com"} - send.prf: UCAN - send.task.meta.data: {tags꞉ ["demo"]} - - send.task.do --> send.do - send.task.with --> send.with - send.task.input --> send.input - send.task.prf --> send.prf - send.task.nnc --> "nonce" - send.task.meta --> send.task.meta.data -} - -state read.task { - direction LR - - read.task.do: do - read.task.with: with - read.task.prf: prf - - - read.do: crud/read - read.with: https꞉//example.com/mailinglist - read.prf1: UCAN - read.prf2: UCAN - - read.task.do --> read.do - read.task.with --> read.with - read.task.prf --> read.prf1 - read.task.prf --> read.prf2 -} - -state update.task { - direction LR - - update.task.do: do - update.task.with: with - update.task.input: input - update.task.prf: prf - update.task.meta: meta - - update.can: crud/update - update.with: dns꞉example.com?TYPE=TXT - update.input: {value꞉"hello world"} - update.prf: UCAN - update.meta: {dev/tags"꞉ ["friends", "coffee"]} - - update.task.do --> update.can - update.task.with --> update.with - update.task.input --> update.input - update.task.prf --> update.prf - update.task.meta --> update.meta - -} - -state send { - send.run: run - send.auth: authorization -} - -state read { - read.run: run - read.cause: cause - read.auth: authorization -} - -state update { - update.run: run - update.cause: cause - update.auth: authorization -} - - -state send.promise { - state send.promise.await <> -} - -state read.promise { - state read.promise.await <> -} - - -state send.receipt { - send.receipt.sig: sig - send.receipt.ran: ran - send.receipt.out: out - send.receipt.fx: fx - send.receipt.meta: meta - send.receipt.iss: iss - send.receipt.prf: prf - -- - - state send.result <> - send.result --> send.result.ok: ok - send.result --> send.result.error: error - - send.result.ok: success - send.result.error: { error } - -- - - send.fx.fork: fork - send.fx.join: join -} - - -send.run --> send.task -send.auth --> auth - -read.run --> read.task -read.auth --> auth - -update.run --> update.task -update.auth --> auth - -scope --> send.task -scope --> read.task -scope --> update.task - - -send.promise.await --> send: ok -send.promise.await --> send: error -send.promise.await --> send: * - -read.promise.await --> read.task: ok -read.promise.await --> read.task: error -read.promise.await --> read.task: * - - -send.receipt.out --> send.result -send.receipt.sig --> send.receipt.ran -send.receipt.sig --> send.receipt.out -send.receipt.sig --> send.receipt.fx -send.receipt.sig --> send.receipt.meta -send.receipt.sig --> send.receipt.iss -send.receipt.sig --> send.receipt.prf - - -send.receipt.fx --> send.fx.fork -send.receipt.fx --> send.fx.join -send.fx.fork --> update.task -send.fx.fork --> read.task -read.cause --> send -update.cause --> send -send.receipt.ran --> send -``` - ### 2.2.1 Task A [Task] is like a deferred function application: a request to perform some action on a resource with specific input. From 9e69c2d849ac740dda460e58d667601782014749 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Thu, 9 Feb 2023 15:46:47 -0800 Subject: [PATCH 146/154] Apply suggestions from code review Signed-off-by: Irakli Gozalishvili --- README.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d3603071..963e7f8b 100644 --- a/README.md +++ b/README.md @@ -262,10 +262,10 @@ type Result union { # Represents a request to invoke enclosed set of tasks concurrently type Effect struct { # Primary set of tasks to be invoked - fork [&Promise] + fork [&Task] # Continuation for straight-line programs - join optional &Promise + join optional &Task } # Promise is an Invocation with optional 'auth' field which if omitted @@ -285,12 +285,6 @@ type Promise struct { prf [&UCAN] } -# Promise is a way to reference result of the invocation -type Await union { - | &Promise "await/*" - | &Promise "await/ok" - | &Promise "await/error" -} representation keyed ``` # 3 Task @@ -808,7 +802,7 @@ Effects describe requests for future work to be performed. All [Invocation]s in [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 guarintees such as failure of one effect impling failure of other effects if left undefined. -The `join` field describes an OPTIONAL "special" [Invocation] that is treated as a "main" thread. Effects MAY instruct the [Executor] that the [Task] [Invocation] is a continuation of the previous Invocation by providing it under `join` field. This roughly emulates a "main" virtual thread. +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. From ce4094814ee0e6b19ad6a6bc00b8ab1342e990f5 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Thu, 9 Feb 2023 15:49:29 -0800 Subject: [PATCH 147/154] Apply suggestions from code review Signed-off-by: Irakli Gozalishvili --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 963e7f8b..2683030e 100644 --- a/README.md +++ b/README.md @@ -195,8 +195,8 @@ An [Effect] are the instruction to the [Executor] to run set of [Task]s concurre ```ipldsch type Task struct { - with URI - do Ability + on URI + call Ability input {String : Any} nnc string From 2c81ea6750a844e0cd957dec5dbdb23b7145a42f Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Thu, 9 Feb 2023 15:53:53 -0800 Subject: [PATCH 148/154] Apply suggestions from code review Signed-off-by: Irakli Gozalishvili --- README.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/README.md b/README.md index 2683030e..458e04f3 100644 --- a/README.md +++ b/README.md @@ -270,20 +270,6 @@ type Effect struct { # Promise is an Invocation with optional 'auth' field which if omitted # is implicitly an 'auth' of the Invocation that contains Await. -type Promise struct { - v SemVer - - run &Task - # Receipt of the invocation that caused this invocation - cause optional &Invocation - # Task authorization. If omitted can be interpreted as requires - # authorization - auth optional &Authorization - - meta {String : any} - - prf [&UCAN] -} ``` From 516003a59b8849b19888f409268ecbf54234fe67 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Fri, 10 Feb 2023 13:38:52 -0800 Subject: [PATCH 149/154] save local edits --- README.md | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 458e04f3..25c22446 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ However, there is clearly a distinction between passing a function and invoking 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 benificial 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. +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 this 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. @@ -195,8 +195,8 @@ An [Effect] are the instruction to the [Executor] to run set of [Task]s concurre ```ipldsch type Task struct { - on URI - call Ability + on URI + call Ability input {String : Any} nnc string @@ -268,9 +268,12 @@ type Effect struct { join optional &Task } -# Promise is an Invocation with optional 'auth' field which if omitted -# is implicitly an 'auth' of the Invocation that contains Await. - +# Way to reference result of the Task +type Await union { + | &Task "await/*" + | &Task "await/ok" + | &Task "await/error" +} representation keyed ``` # 3 Task @@ -281,8 +284,8 @@ Using the JavaScript analogy from the introduction, a Task is similar to wrappin ```json { - "do": "msg/send", - "with": "mailto:alice@example.com", + "on": "mailto:alice@example.com", + "call": "msg/send", "input": { "to": [ "bob@example.com", @@ -308,17 +311,17 @@ Later, when we explore promise [pipelines], this also includes capturing the pro ```json { - "bafyreid32aunjg6g6buib7bva2sa5ni4c6eaftgykyh6z6rnhcjxh4yk2y": { - "do": "crud/read", - "with": "https://exmaple.com/mailinglist" + "bafyreieuo63r3y2nuycaq4b3q2xvco3nprlxiwzcfp4cuupgaywat3z6mq": { + "on": "https://exmaple.com/mailinglist", + "call": "crud/read" }, - "bafyreigfusg7tgegda7pxdn7px5vs7wxqk5nybr5ewjht4xas357x6ryia": { - "do": "msg/send", - "with": "mailto://alice@example.com", + "bafyreihtmwju3okftpeuqe3x3ux5e7c2jescakwnoiyv45vnicke4kdxy4": { + "on": "mailto://alice@example.com", + "call": "msg/send", "input": { "to": { "await/ok": { - "/": "bafyreifkeu34pzda27fozfcboh25psekhqehkvnekltoiaspb5jsp6pj5q" + "/": "bafyreieuo63r3y2nuycaq4b3q2xvco3nprlxiwzcfp4cuupgaywat3z6mq" } }, "subject": "hello", @@ -404,8 +407,8 @@ The `prf` field MUST contain links to any UCANs that provide the authority to pe ```json { - "do": "crud/create", - "with": "https://example.com/blog/posts", + "on": "https://example.com/blog/posts", + "call": "crud/create", "input": { "headers": { "content-type": "application/json" @@ -427,8 +430,8 @@ The `prf` field MUST contain links to any UCANs that provide the authority to pe ```json { - "do": "msg/send", - "with": "mailto:akiko@example.com", + "on": "mailto:akiko@example.com", + "call": "msg/send", "input": { "to": [ "boris@example.com", @@ -444,8 +447,8 @@ The `prf` field MUST contain links to any UCANs that provide the authority to pe ```json { - "do": "wasm/run", - "with": "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==", + "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==", + "call": "wasm/run", "input": { "func": "add_one", "args": [ @@ -782,6 +785,8 @@ If no information is available, this field SHOULD be set to `{}`. } ``` +## 7 Effect + 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 reuqesting 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`. From 06c92590668ef688774db03bd253ec95509d82e9 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Fri, 10 Feb 2023 15:42:06 -0800 Subject: [PATCH 150/154] update all examples and fields --- README.md | 1074 +++++++++++++++++++++++------------------------------ 1 file changed, 460 insertions(+), 614 deletions(-) diff --git a/README.md b/README.md index 25c22446..9345e7c5 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ A [Task] is like a deferred function application: a request to perform some acti ### 2.2.2 Authorization -An [Authorization] is a cryptographically signed proof permitting execution of referenced tasks. It allows the [Invoker] to authorize a group of tasks using one cryptographic signature. +An [Authorization] is a cryptographically signed proof permitting execution of referenced [Task]s. It allows the [Invoker] to authorize a group of tasks using one cryptographic signature. ### 2.2.3 Invocation @@ -185,7 +185,7 @@ A [Result] is the output of a [Task]. ### 2.2.5 Receipt -A [Receipt] is a cryptographically signed description of the [Invocation] output. +A [Receipt] is a cryptographically signed description of the [Invocation] output and requested [Effect]s. ### 2.2.6 Effect @@ -225,6 +225,7 @@ type Invocation struct { prf [&UCAN] } + type SemVer string type Receipt struct { @@ -345,32 +346,22 @@ const sendEmail = msg.send("mailto://alice@example.com", { ```ipldsch type Task struct { - with URI - do Ability - input {String: any} - - nnc string + on URI + call Ability + input {String:Any} + nnc string } - - -type URI string -type Ability string - ``` ## 3.2 Fields -### 3.2.1 UCAN Task Version - -The `v` field MUST contain the SemVer-formatted version of the UCAN Task Specification that this struct conforms to. +### 3.2.1 Resource -### 3.2.2 Resource - -The `with` 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 `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. ### 3.2.3 Ability -The `do` 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 `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 @@ -387,20 +378,10 @@ UCAN capabilities provided in [Proofs] MAY impose certain constraint on the type If `input` field is not present, it is implicitly a `unit` represented as empty map. -### 3.2.5 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. - ### 3.2.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.2.7 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. - ## 3.3 DAG-JSON Examples ### 3.3.1 Interacting with an HTTP API @@ -460,7 +441,7 @@ The `prf` field MUST contain links to any UCANs that provide the authority to pe # 4 Authorization -An [Authorization] is cryptographically signed data set. It represents an authorization to run [Task]s in that are included in `scope` data set. +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 @@ -491,15 +472,15 @@ The `s` field MUST contain a [Varsig] of the [CBOR] encoded `scope` field. { "scope": [ { - "/": "bafyreigfusg7tgegda7pxdn7px5vs7wxqk5nybr5ewjht4xas357x6ryia" + "/": "bafyreihtmwju3okftpeuqe3x3ux5e7c2jescakwnoiyv45vnicke4kdxy4" }, { - "/": "bafyreid32aunjg6g6buib7bva2sa5ni4c6eaftgykyh6z6rnhcjxh4yk2y" + "/": "bafyreieuo63r3y2nuycaq4b3q2xvco3nprlxiwzcfp4cuupgaywat3z6mq" } ], "s": { "/": { - "bytes": "7aEDQJBsO/9rkcgQQ9qvSC0E60MdqGjpIHXuc4VbkfjEeYR7iAvAY+0QEN9kAFcwh6/kww4bK/I7xsqLzO5kG0x+2Qs" + "bytes": "7aEDQIJB8XXJ6hWbwu40fN4bq8+Zq8BxyybSWXatMVU3VsL+yzVYpeJqsEBQE5rNtUJefR5rRCNimKNZMJjA9/udZQQ" } } } @@ -534,17 +515,31 @@ type SemVer string ## 5.2 Fields -### 5.2.1 Task +### 5.2.1 UCAN Task Version + +The `v` field MUST contain the SemVer-formatted version of the UCAN Invocation Specification that this struct conforms to. + +### 5.2.2 Task The `run` field MUST contain a link to the [Task] to be run. -### 5.2.2 Authorization +### 5.2.3 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.4 Authorization 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.2.3 Cause +### 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. -[Task]s MAY be invoked as an effect caused by a prior [Invocation]. Such [Invocation]s SHOULD have a link to the [Invocation] that caused it in the `cause` field. The [Receipt] of the causing [Invocation] (Invocation linked from `cause` field) MUST have an `Effect` (the `fx` field) containing caused [Invocation]. +If `meta` field is not present, it is implicitly a `unit` represented as an empty map. ## 5.3 DAG-JSON Example @@ -552,57 +547,50 @@ The `auth` field MUST contain a link to the [Authorization] that authorizes invo ```json { - "blocks": { - "bafyreibgqjpwjks2dh2zgvq5ypuh5bly6quoi2dxjzfoa676owl6tscz5q": { - "do": "crud/create", - "with": "https://example.com/blog/posts", - "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 - } + "bafyreia5gmhblitl6bmggheh4yh5z3uws3anlpr3ieexd44p74fq56746e": { + "on": "https://example.com/blog/posts", + "call": "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 } + } + }, + "bafyreic52he5oyk5qje3rscieok5ucdgl2maibx7mecumjtsf7cosx2fum": { + "v": "0.1.0", + "run": { + "/": "bafyreia5gmhblitl6bmggheh4yh5z3uws3anlpr3ieexd44p74fq56746e" }, - "bafyreifpdtrqxeqvg5r5ctvcihnsrregsnw3gi74unbl66yf3dio2fee4a": { - "v": "0.1.0", - "run": { - "/": "bafyreibgqjpwjks2dh2zgvq5ypuh5bly6quoi2dxjzfoa676owl6tscz5q" - }, - "auth": { - "/": "bafyreifpvyetnerhqoqdijxx4kf6hhofaz7momudq52scepsxzksshxm5a" - }, - "prf": [ - { - "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" - } - ] + "auth": { + "/": "bafyreiftkrpdnijison6ttacz4s3qorklp7a3653hq4uaoyznsn6al4rke" }, - "bafyreifpvyetnerhqoqdijxx4kf6hhofaz7momudq52scepsxzksshxm5a": { - "scope": [ - { - "/": "bafyreibgqjpwjks2dh2zgvq5ypuh5bly6quoi2dxjzfoa676owl6tscz5q" - } - ], - "s": { - "/": { - "bytes": "7aEDQDqN6XjxQnEXQLlg03zRXpoIpA/0ldLCVHA0hDdJLLQlRvQoh4Q24eAL3mozij08vTcKQhkvqClAuJ9FJr1eNAo" - } + "prf": [ + { + "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" } - } + ] }, - "roots": [ - { - "/": "bafyreifpdtrqxeqvg5r5ctvcihnsrregsnw3gi74unbl66yf3dio2fee4a" + "bafyreiftkrpdnijison6ttacz4s3qorklp7a3653hq4uaoyznsn6al4rke": { + "scope": [ + { + "/": "bafyreia5gmhblitl6bmggheh4yh5z3uws3anlpr3ieexd44p74fq56746e" + } + ], + "s": { + "/": { + "bytes": "7aEDQPPhXNvtVb5/T+O40xXU6TSgJZDFnlVaV3GMlaEo/dvxtyaCLm8uUsFK4xzQsQd82QQUYA6fK506XqjghRlucAQ" + } } - ] + } } ``` @@ -610,89 +598,79 @@ The `auth` field MUST contain a link to the [Authorization] that authorizes invo ```json { - "blocks": { - "bafyreibgqjpwjks2dh2zgvq5ypuh5bly6quoi2dxjzfoa676owl6tscz5q": { - "do": "crud/create", - "with": "https://example.com/blog/posts", - "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 - } - } - }, - "bafyreiefy3rmuhla7uoxsjbv2zmz2ysmgy3erigwiyckcglxes6sgywms4": { - "do": "msg/send", - "with": "mailto:akiko@example.com", - "input": { - "to": [ - "boris@example.com", - "carol@example.com" + "bafyreia5gmhblitl6bmggheh4yh5z3uws3anlpr3ieexd44p74fq56746e": { + "on": "https://example.com/blog/posts", + "call": "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" ], - "body": "Hey you two, I'd love to get coffee sometime and talk about UCAN Tasks!", - "subject": "Coffee" + "draft": true } + } + }, + "bafyreie5rfou2tubwot7laoicmm6qkbqltbae6uqsh3wo6zmhwjpjdwhii": { + "on": "mailto:akiko@example.com", + "call": "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" + } + }, + "bafyreiei3dbiv5mu5zymm6socbcfafg4d43lstizd6eomz2hv6y4o6nkpm": { + "v": "0.1.0", + "run": { + "/": "bafyreia5gmhblitl6bmggheh4yh5z3uws3anlpr3ieexd44p74fq56746e" }, - "bafyreiaf4vq3ia7ykisqaxb3oxxjgam226b5juzia6xmrpjgodpief42cq": { - "v": "0.1.0", - "run": { - "/": "bafyreibgqjpwjks2dh2zgvq5ypuh5bly6quoi2dxjzfoa676owl6tscz5q" - }, - "auth": { - "/": "bafyreiatkpx7p4m2jlry547in4asajlyqncmvpiykprgyd6uqfwlyvfirm" - }, - "prf": [ - { - "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" - } - ] - }, - "bafyreifpjetehajrap63g45djib2mxdzlr5wl442kbqgwpozfoiz76535u": { - "v": "0.1.0", - "run": { - "/": "bafyreiefy3rmuhla7uoxsjbv2zmz2ysmgy3erigwiyckcglxes6sgywms4" - }, - "auth": { - "/": "bafyreiatkpx7p4m2jlry547in4asajlyqncmvpiykprgyd6uqfwlyvfirm" - }, - "prf": [ - { - "/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4" - } - ] + "auth": { + "/": "bafyreiaey7rgkvgegx2ylrtfgfl7lwfoagpessjxoxppeu4gkn2cgdbh7y" }, - "bafyreiatkpx7p4m2jlry547in4asajlyqncmvpiykprgyd6uqfwlyvfirm": { - "scope": [ - { - "/": "bafyreiefy3rmuhla7uoxsjbv2zmz2ysmgy3erigwiyckcglxes6sgywms4" - }, - { - "/": "bafyreibgqjpwjks2dh2zgvq5ypuh5bly6quoi2dxjzfoa676owl6tscz5q" - } - ], - "s": { - "/": { - "bytes": "7aEDQFbdUsz5BvcCGkzwVtthNoQI/bxXi2CO+bmlFIbmfll/5+40dadU6sIY2vUfxYgUywprct3H+yIYIp0HuNdgxQM" - } + "prf": [ + { + "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" } - } + ] }, - "roots": [ - { - "/": "bafyreiaf4vq3ia7ykisqaxb3oxxjgam226b5juzia6xmrpjgodpief42cq" + "bafyreihoo7w6jpo43lqulfc3orcvutbsizjoja7uch65h74gf5rbtn5gdy": { + "v": "0.1.0", + "run": { + "/": "bafyreie5rfou2tubwot7laoicmm6qkbqltbae6uqsh3wo6zmhwjpjdwhii" }, - { - "/": "bafyreifpjetehajrap63g45djib2mxdzlr5wl442kbqgwpozfoiz76535u" + "auth": { + "/": "bafyreiaey7rgkvgegx2ylrtfgfl7lwfoagpessjxoxppeu4gkn2cgdbh7y" + }, + "prf": [ + { + "/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4" + } + ] + }, + "bafyreiaey7rgkvgegx2ylrtfgfl7lwfoagpessjxoxppeu4gkn2cgdbh7y": { + "scope": [ + { + "/": "bafyreie5rfou2tubwot7laoicmm6qkbqltbae6uqsh3wo6zmhwjpjdwhii" + }, + { + "/": "bafyreia5gmhblitl6bmggheh4yh5z3uws3anlpr3ieexd44p74fq56746e" + } + ], + "s": { + "/": { + "bytes": "7aEDQMyGqYw2iwP7uIn+Kav5AWe9l5VnL72Gpkzs1Azp+zs6vnixQPa1aCSrok4XwKkhSlFRmRN8YbyohB6iDFl4CQ8" + } } - ] + } } ``` @@ -700,49 +678,42 @@ The `auth` field MUST contain a link to the [Authorization] that authorizes invo ```json { - "blocks": { - "bafyreifcerdvicarktlnif5uj25ultgpedwg63nxhmp7anoepaamqj4eji": { - "do": "crud/update", - "with": "dns:example.com?TYPE=TXT", - "input": { - "value": "hello world" - } + "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny": { + "on": "dns:example.com?TYPE=TXT", + "call": "crud/update", + "input": { + "value": "hello world" + } + }, + "bafyreiemzjyz5sxmeatnlykpzr2feribz6yyxjlvvufsrbodx43t4wfi5m": { + "v": "0.1.0", + "run": { + "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" }, - "bafyreicc3rilzaxravqju766yr4v7p6nm6sjsd4lbkfa4uqohbbxaejh7e": { - "v": "0.1.0", - "run": { - "/": "bafyreifcerdvicarktlnif5uj25ultgpedwg63nxhmp7anoepaamqj4eji" - }, - "auth": { - "/": "bafyreifw2hxvz66fxx7vsn6q3ayrc62smhvt7jta3ck2u6kf4xgqo524hm" - }, - "cause": { - "/": "bafyreiekrhjlfend2mwcmtakwshuctotwqcm2wriya6thpjcgvjzfmkt24" - }, - "prf": [ - { - "/": "bafyreieynwqrabzdhgl652ftsk4mlphcj3bxchkj2aw5eb6dc2wxieilau" - } - ] + "auth": { + "/": "bafyreia6tgroy43hkzywlg4ks7zxyvzgc62wifdvqjyy6uo3hrrrehgb3u" }, - "bafyreifw2hxvz66fxx7vsn6q3ayrc62smhvt7jta3ck2u6kf4xgqo524hm": { - "scope": [ - { - "/": "bafyreifcerdvicarktlnif5uj25ultgpedwg63nxhmp7anoepaamqj4eji" - } - ], - "s": { - "/": { - "bytes": "7aEDQEkocMddH2BF4wuMRSlJaJFhu7pQaAInuzLrfU3TQd9XV/Rd0I6N6OvxPaCebNfpuCXrAgtlAxVh+sn8gWSSegk" - } + "cause": { + "/": "bafyreibytdkxnyy4zzkiznz4psosqdhazizjrllzmu373ej4r6bckcvwju" + }, + "prf": [ + { + "/": "bafyreieynwqrabzdhgl652ftsk4mlphcj3bxchkj2aw5eb6dc2wxieilau" } - } + ] }, - "roots": [ - { - "/": "bafyreicc3rilzaxravqju766yr4v7p6nm6sjsd4lbkfa4uqohbbxaejh7e" + "bafyreia6tgroy43hkzywlg4ks7zxyvzgc62wifdvqjyy6uo3hrrrehgb3u": { + "scope": [ + { + "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" + } + ], + "s": { + "/": { + "bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0" + } } - ] + } } ``` @@ -815,62 +786,62 @@ type Effect { ### 7.2.1 Forked Task Invocations -The OPTIONAL `fork` field, if present MUST be a list of an alphabetically ordered [Invocation] links. List MUST NOT not contain duplicate entries. Every linked [Invocation] MUST have `cause` field set to the same [Invocation] (link) as the `run` field of the containing [Receipt]. +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 an [Invocation] link. Linked [Invocation] in the `join` field MUST have `cause` field set to the same [Invocation] (link) as the `run` field of the containing [Receipt]. +The OPTIONAL `join` field, if present MUST be set to a [Task] link. -## 7.4 DAG-JSON Examples +## 7.3 DAG-JSON Examples -### 7.4.1 Effect spawning concurrent threads +### 7.3.1 Effect spawning concurrent threads ```json { "fork": [ { - "/": "bafyreigtbbrfic7gduybn2lcncbbzycbef3kt3aqeguko5dmuhpmf73zmu" + "/": "bafyreigmmdzix2vxboojvv6j6h7sgvxnrecdxtglwtqpxw7hybebzlsax4" }, { - "/": "bafyreidj2fkidh5g4tlldael7dfw27va6vbn6ah6pv7hirsandzwp25pj4" + "/": "bafyreif6gfpzgxnii4ys6a4bjenefg737fb5bgam3onrbmhnoa4llk244q" } ] } ``` -### 7.4.2 Effect continuing thread execution +### 7.3.2 Effect continuing thread execution ```json { "join": { - "/": "bafyreigqsmtpw6qliojfzupzbxsnkn4yave7vov2jvvrtye7w4nomvd7yq" + "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" } } ``` -### 7.4.1 Effect with fork & join +### 7.3.1 Effect with fork & join ```json { + "join": { + "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" + }, "fork": [ { - "/": "bafyreieljernknkcrmefokxxperobictfndo4v4cazkjmqfwuog4puawxm" + "/": "bafyreigmmdzix2vxboojvv6j6h7sgvxnrecdxtglwtqpxw7hybebzlsax4" }, { - "/": "bafyreiho3x2lbi6a5irw3orgrzrivkakr7iymalpukmxsvdsoqlxpcjsfq" + "/": "bafyreif6gfpzgxnii4ys6a4bjenefg737fb5bgam3onrbmhnoa4llk244q" } - ], - "join": { - "/": "bafyreidtsxqd5fcuwdgverklwpcgbgzixsf6z6wgelybiakpwec3q75iwa" - } + ] } ``` # 8 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 Issuer (the `iss` of the receipt) MUST be provided in `prf`. +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`. -**NB: a Receipt this does not guarantee correctness of the result!** The statement's veracity MUST be only understood as an attestation from the executor. +**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. @@ -913,27 +884,27 @@ The `out` field MUST contain the value output of the invocation in [Result] form ### 8.2.3 Effect -The OPTIONAL `fx` field, if present MUST be set to the caused [Effect]. The [Executor] SHOULD perform contained [Task] [Invocation]s to progress a workflow execution. +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 -The 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. +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 -The OPTIONAL `iss` field, if present MUST contain the signer of the receipt. It MUST be an [Executor] or it's delegate. If delegate proof of delegation MUST be provided in `prf` field. +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, it MUST implicitly imply an [Executor]. +If `iss` field is omitted, Receipt MUST be signed by the [Executor]. ### 8.2.6 Proofs -The `prf` field MUST contain links to UCAN(s) that that delegate authority to perform the invocation from the [Executor] to the Receipt issuer (`iss`). If [Executor] and the Issuer are same no proofs are required. +If OPTIONAL `prf` field is present, MUST contain link to UCAN delegatation authorizing Receipt Issuer (`iss`) to carry [Task] execution. ### 8.2.7 Signature -The `s` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without `s` field. The signature MUST be generated by the issuer (`iss`). +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. ## 8.3 DAG-JSON Examples @@ -942,7 +913,7 @@ The `s` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without ```json { "ran": { - "/": "bafyreifkeu34pzda27fozfcboh25psekhqehkvnekltoiaspb5jsp6pj5q" + "/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu" }, "out": { "ok": { @@ -961,7 +932,7 @@ The `s` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without }, "s": { "/": { - "bytes": "7aEDQIeyhI1chc8p3jpODozaxomV3y+rym9f+G7kIxyjsV0bqZZELGeCaYhIMYSmXk5aSAdufXJwLCFT6vsbm7DDqA0" + "bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU" } } } @@ -972,7 +943,7 @@ The `s` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without ```json { "ran": { - "/": "bafyreifkeu34pzda27fozfcboh25psekhqehkvnekltoiaspb5jsp6pj5q" + "/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu" }, "out": { "ok": { @@ -997,18 +968,18 @@ The `s` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without ], "s": { "/": { - "bytes": "7aEDQB1URqWf6LhVUhbSgCOERp5wEGH0sEPqGvLrYpwsh5QslqVrwW8EXMjlRgeKeyHkM7yXxqzTXdnkB0tqFgMYOAk" + "bytes": "7aEDQKxIrga+88HNDd69Ho4Ggz8zkf+GxWC6dAGYua6l85YgiL3NqGxyGAygiSZtWrWUo6SokgOys2wYE7N+novtcwo" } } } ``` -### 7.3.3 Receipts with effects +### 7.3.3 Receipt with effects ```json { "ran": { - "/": "bafyreicjvdt44rclleycshdpkpsnxpksztn5bmx6w6ximjmod7osuvs76i" + "/": "bafyreig3qnao4suz3lchh4joof7fhlobmgxhaal3vw4vtcghtlgtp7u4xy" }, "out": { "ok": { @@ -1016,21 +987,21 @@ The `s` field MUST contain a [Varsig] of the [DAG-CBOR] encoded Receipt without } }, "fx": { + "join": { + "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" + }, "fork": [ { - "/": "bafyreieljernknkcrmefokxxperobictfndo4v4cazkjmqfwuog4puawxm" + "/": "bafyreigmmdzix2vxboojvv6j6h7sgvxnrecdxtglwtqpxw7hybebzlsax4" }, { - "/": "bafyreiho3x2lbi6a5irw3orgrzrivkakr7iymalpukmxsvdsoqlxpcjsfq" + "/": "bafyreif6gfpzgxnii4ys6a4bjenefg737fb5bgam3onrbmhnoa4llk244q" } - ], - "join": { - "/": "bafyreidtsxqd5fcuwdgverklwpcgbgzixsf6z6wgelybiakpwec3q75iwa" - } + ] }, "s": { "/": { - "bytes": "7aEDQMhFF0SuvqImvMRUAORuY5uJttauHMRvt0kwFRXkidPqvkrouDr49lBkMKrGrCcJslpcp+/MeG0FoTjxdo3KQQc" + "bytes": "7aEDQAHWabtCE+QikM3Np94TrA5T8n2yXqy8Uf35hgw0fe5c2Xi1O0h/JgrFmGl2Gsbhfm05zpdQmwfK2f/Sbe00YQE" } } } @@ -1046,104 +1017,75 @@ There MAY not be enough information to described an Invocation at creation time. 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. -An [Await] MAY be used as a variable placeholder for a concrete value in an [Invocation] output, waiting on a previous step to complete. +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. For example, consider the following invocation batch: ```json { - "blocks": { - "bafyreicxndtwegag3mmc6vos7xponkb2pkm7x5p5clbbgbheyuxssam6ka": { - "do": "crud/create", - "with": "https://example.com/blog/posts", - "input": { - "payload": { - "title": "How UCAN Tasks Changed My Life", - "body": "This is the story of how one spec changed everything..." - } + "bafyreiftw26kcrsf4tdijatgqxq6gtfhtrneetrcrp27ks6rvzsvzyanj4": { + "on": "https://example.com/blog/posts", + "call": "crud/create", + "input": { + "payload": { + "title": "How UCAN Tasks Changed My Life", + "body": "This is the story of how one spec changed everything..." } - }, - "bafyreici7ms2diig6y3ju64f3y5q3rn4zezbmxoiv3hwxleaoia6sg5v2u": { - "v": "0.1.0", - "run": { - "/": "bafyreicxndtwegag3mmc6vos7xponkb2pkm7x5p5clbbgbheyuxssam6ka" - }, - "prf": [ - { - "/": "bafyreid6q7uslc33xqvodeysekliwzs26u5wglas3u4ndlzkelolbt5z3a" + } + }, + "bafyreifsvtyuiddvpsvurkwhyw3w55sp3nkw66gji6xxti5czol2q4hdqm": { + "on": "https://example.com/users/editors", + "call": "crud/read" + }, + "bafyreiep7bzrz3irallgmsd43kipuh32khteq2dne4qc763txmvb2zmevu": { + "on": "mailto:akiko@example.com", + "call": "msg/send", + "input": { + "to": { + "await/ok": { + "/": "bafyreiftw26kcrsf4tdijatgqxq6gtfhtrneetrcrp27ks6rvzsvzyanj4" } - ] - }, - "bafyreibsgkiansdaty6eeuwtutmx6ztyp2wcmucoisyxzdlfeo74rhvuvi": { - "do": "crud/read", - "with": "https://example.com/users/editors" - }, - "bafyreiamphjpf5bwht6le4v2fsbxa55omerv3lpoyhi3bzzimcrii7vfri": { - "v": "0.1.0", - "run": { - "/": "bafyreibsgkiansdaty6eeuwtutmx6ztyp2wcmucoisyxzdlfeo74rhvuvi" }, - "prf": [ - { - "/": "bafyreie3ukg4h2kf7lnx7k62kjujlo2a5l66rh7e7vlj52fnsrj7tuc2ya" - } - ] - }, - "bafyreiedbqqzfbo7kqdu3gjfcmfq7ffhzbs37qmu4q73mgtwm7h54jfbsm": { - "do": "msg/send", - "with": "mailto:akiko@example.com", - "input": { - "to": { - "await/ok": { - "/": "bafyreici7ms2diig6y3ju64f3y5q3rn4zezbmxoiv3hwxleaoia6sg5v2u" - } - }, - "subject": "Coffee", - "body": { - "await/ok": { - "/": "bafyreiamphjpf5bwht6le4v2fsbxa55omerv3lpoyhi3bzzimcrii7vfri" - } + "subject": "Coffee", + "body": { + "await/ok": { + "/": "bafyreifsvtyuiddvpsvurkwhyw3w55sp3nkw66gji6xxti5czol2q4hdqm" } } + } + }, + "bafyreiau3ygtmtv4jfkl4dgg6j6vhw5kwty5undybijexm35wmazgbb2tq": { + "v": "0.1.0", + "run": { + "/": "bafyreiep7bzrz3irallgmsd43kipuh32khteq2dne4qc763txmvb2zmevu" }, - "bafyreic7ypuk4xd4kvintknoxfgy33ttkr3kv2vp2fenpylecofhuswiye": { - "v": "0.1.0", - "run": { - "/": "bafyreiedbqqzfbo7kqdu3gjfcmfq7ffhzbs37qmu4q73mgtwm7h54jfbsm" - }, - "auth": { - "/": "bafyreifz5u3yzkophibkf4w23doou4pvhmqdje27cwihnl7obsbyeo3qym" - }, - "prf": [ - { - "/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4" - } - ] + "auth": { + "/": "bafyreibvtceb6flhmazpufpro6fneaqn7de7wxju74l2kuptwhxjyuecja" }, - "bafyreifz5u3yzkophibkf4w23doou4pvhmqdje27cwihnl7obsbyeo3qym": { - "scope": [ - { - "/": "bafyreiedbqqzfbo7kqdu3gjfcmfq7ffhzbs37qmu4q73mgtwm7h54jfbsm" - }, - { - "/": "bafyreicxndtwegag3mmc6vos7xponkb2pkm7x5p5clbbgbheyuxssam6ka" - }, - { - "/": "bafyreibsgkiansdaty6eeuwtutmx6ztyp2wcmucoisyxzdlfeo74rhvuvi" - } - ], - "s": { - "/": { - "bytes": "7aEDQJb+yWxXmprXwpBSvvMtyGjG75QHA90UkQduagjS1DpQnqE4NB3Bm41j7q8ODIKBnPXtZAHBaX/VxGUJvD8DXAo" - } + "prf": [ + { + "/": "bafyreihvee5irbkfxspsim5s2zk2onb7hictmpbf5lne2nvq6xanmbm6e4" } - } + ] }, - "roots": [ - { - "/": "bafyreic7ypuk4xd4kvintknoxfgy33ttkr3kv2vp2fenpylecofhuswiye" + "bafyreibvtceb6flhmazpufpro6fneaqn7de7wxju74l2kuptwhxjyuecja": { + "scope": [ + { + "/": "bafyreiftw26kcrsf4tdijatgqxq6gtfhtrneetrcrp27ks6rvzsvzyanj4" + }, + { + "/": "bafyreifsvtyuiddvpsvurkwhyw3w55sp3nkw66gji6xxti5czol2q4hdqm" + }, + { + "/": "bafyreiep7bzrz3irallgmsd43kipuh32khteq2dne4qc763txmvb2zmevu" + } + ], + "s": { + "/": { + "bytes": "7aEDQDEGkezG7Bcpeknf2UJ7hpqeL1PZodrYYTSwRjqZPf67P4r1lRZvX+6+9gV+wDZUX0DZLMv64n2fPKnjvxrEugE" + } } - ] + } } ``` @@ -1166,89 +1108,53 @@ const notify = msg.send("mailto:akiko@example.com", { }) ``` -While an [Await] MAY be substituted for any field in a [Task], substituting the `do` field is NOT RECOMMENDED. The `do` field is critical in understanding what kind of action will be performed, and schedulers SHOULD use this fields to grant atomicity, parallelize tasks, and so on. - -After resolution, the [Invocation] MUST be validated against the [Authorization] and linked UCAN proofs by the [Executor]. A Promise resolved to an [Invocation] that is not backed by a valid UCAN MUST NOT be executed, and SHOULD return an unauthorized error to the user. A Promise resolved to an [Invocation] with the [Authorization] that does not include invoked [Task] MUST NOT be executed, and SHOULD return an unauthorized error to the user. - -Promises 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). - -## 9.2 Promise - -A `Promise` has a same representation as [Invocation] with only difference being that `auth` field is OPTIONAL. Promise MAY be used by an [Invocation] to [Await] another [Invocation] authorized with the same [Authorization]. [Invocation] MAY be used directly everywhere else in place of the [Promise]. - -### 9.2.1 Schema - -```ipldsch -# Promise is an Invocation with optional 'auth' field which if omitted -# is implicitly an 'auth' of the Invocation that contains Await. -type Promise struct { - v SemVer - - run &Task - # Receipt of the invocation that caused this invocation - cause optional &Invocation - # Task authorization. If omitted can be interpreted as requires - # authorization - auth optional &Authorization - - meta {String : any} - - prf [&UCAN] -} - -``` - -## 9.3 Await +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 substituded with `Await`. -An `Await` describes the eventual output of the referenced [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. +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 [Invocation] from the [Await] is resolved by: +## Await -1. Creating a new [Invocation]. -1. Setting all of the fields, but `auth` to same values as an underlying [Promise]. -1. If OPTIONAL `auth` field of the underlying [Promise] is set: - - then set `auth` of the invocation to the same value - - else set `auth` of the invocation to the `auth` of the [Invocation] containing [Await] been resolved. +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.3.1 Schema +### 9.1 Schema ```ipldsch type Await union { - | &Promise "await/*" - | &Promise "await/ok" - | &Promise "await/error" + | &Task "await/*" + | &Task "await/ok" + | &Task "await/error" } representation keyed ``` -#### 9.3.2 Variants +#### 9.2 Variants -##### 9.3.2.1 Success +##### 9.2.1 Success -The successful output of the [Invocation] MAY be referenced by wrapping corresponding [Promise] in the `"await/ok"` tag. +The successful output of the [Task] MAY be referenced by wrapping the [Task] in the `"await/ok"` tag. -[Executor] MUST fail [Invocation] that `Await`s successful output of the failed [Invocation]. +[Executor] MUST fail [Task] that `Await`s successful output of the failed [Task]. -[Executor] MUST substitute [Task] field set to the [Await] of the successful [Invocation] with an (unwrapped) `ok` value of the output. +[Executor] MUST substitute [Task] field set to the [Await] of the successful [Task] with an (unwrapped) `ok` value of the output. -##### 9.3.2.1 Failure +##### 9.2.2 Failure -The failed output of the [Invocation] MAY be referenced by wrapping corresponding [Promise] in the `"await/error"` tag. +The failed output of the [Task] MAY be referenced by wrapping the [Task] in the `"await/error"` tag. -[Executor] MUST fail [Invocation] that `Await`s failed output of the successful [Invocation]. +[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 [Invocation] with an (unwrapped) `error` value of the output. +[Executor] MUST substitute [Task] field set to the [Await] of the failed [Task] with an (unwrapped) `error` value of the output. -##### 9.3.2.1 Result +##### 9.2.3 Result -The [Result] output of the [Invocation] MAY be reference by wrapping corresponding [Promise] in the `"await/*"` tag. +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 [Invocation] with a `Result` value of the output. +[Executor] MUST substitute [Task] field set to the [Await] of the [Task] with a `Result` value of the output. -## 9.4 Dataflow +## 9.3 Dataflow Pipelining uses [Await] as inputs to determine the required dataflow graph. The following examples both express the following dataflow graph: -### 9.4.1 Batched +### 9.3.1 Batched ```mermaid flowchart BR @@ -1270,123 +1176,100 @@ flowchart BR ```json { - "blocks": { - "bafyreiesse5saoa5dj7f5mh7sffy57vfhnjm6tpgwmwxe3ncwin2hwqsoy": [ - { "/": "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli" }, - { "/": "bafyreier7y2snffy7x75y4ri6ahiflxmz7ksklizzwpphwgpfotc3omo2y" } - ], - "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli": { - "v": "0.1.0", - "with": "dns:example.com?TYPE=TXT", - "do": "crud/update", - "input": { - "value": "hello world" + "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny": { + "on": "dns:example.com?TYPE=TXT", + "call": "crud/update", + "input": { + "value": "hello world" + } + }, + "bafyreihbli7vcw2n42xqv43ushojh7nvto6zpb3rd5ekoo6mim6bfkkqku": { + "on": "mailto://alice@example.com", + "call": "msg/send", + "input": { + "to": "bob@example.com", + "subject": "DNSLink for example.com", + "body": { + "await/ok": { + "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" + } + } + } + }, + "bafyreifiwxa2mnjvbihr45q56j6vyy4ksml7fh2tq2wnqtf5n55yveevja": { + "on": "mailto://alice@example.com", + "call": "msg/send", + "input": { + "to": "carol@example.com", + "subject": "Hey Carol, DNSLink was updated!", + "body": { + "await/ok": { + "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" + } + } + } + }, + "bafyreiail3bkoyow46d6gnisj4dttiitifiaodee3ixynbhyq6vzxnvj2q": { + "on": "https://example.com/report", + "call": "crud/update", + "input": { + "payload": { + "from": "mailto://alice@exmaple.com", + "to": [ + "bob@exmaple.com", + "carol@example.com" + ], + "event": "email-notification" }, - "prf": [ - { "/": "bafyreieynwqrabzdhgl652ftsk4mlphcj3bxchkj2aw5eb6dc2wxieilau" } - ] - }, - "bafyreieeipburgtzg4hr2ghxgspc53szrkstaz4syjat3tex75hjdrau3y": [ - { "/": "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm" }, - { "/": "bafyreier7y2snffy7x75y4ri6ahiflxmz7ksklizzwpphwgpfotc3omo2y" } - ], - "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm": { - "v": "0.1.0", - "with": "mailto://alice@example.com", - "do": "msg/send", - "input": { - "subject": "DNSLink for example.com", - "to": "bob@example.com", - "body": { + "_": [ + { "await/ok": { - "ucan/task": { - "/": "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli" - } + "/": "bafyreihbli7vcw2n42xqv43ushojh7nvto6zpb3rd5ekoo6mim6bfkkqku" } - } - }, - "prf": [ - { "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" } - ] - }, - "bafyreif7cskac7ongewanqahk2vljkk5rsiblm2vqqo57j2d6mgeuhtuxq": [ - { "/": "bafyreifjjvdd6hoi7wjot7tjaubwztqldds332bgq6a4n37d6s5slxkbvy" }, - { "/": "bafyreier7y2snffy7x75y4ri6ahiflxmz7ksklizzwpphwgpfotc3omo2y" } - ], - "bafyreifjjvdd6hoi7wjot7tjaubwztqldds332bgq6a4n37d6s5slxkbvy": { - "v": "0.1.0", - "with": "mailto://alice@example.com", - "do": "msg/send", - "input": { - "to": "carol@example.com", - "subject": "Hey Carol, DNSLink was updated!", - "body": { + }, + { "await/ok": { - "ucan/task": { - "/": "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli" - } + "/": "bafyreifiwxa2mnjvbihr45q56j6vyy4ksml7fh2tq2wnqtf5n55yveevja" } } - }, - "prf": [ - { "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" } ] + } + }, + "bafyreid2esrl52jp5rx6kh7opwlc2jnzhci7yd5jtlzwlqytujk6y6urza": { + "v": "0.1.0", + "run": { + "/": "bafyreiail3bkoyow46d6gnisj4dttiitifiaodee3ixynbhyq6vzxnvj2q" }, - "bafyreid5bb7z7l4ti57gdep6tbsnam47555ivnh3znpylo2n7qqyiiggqm": [ - { "/": "bafyreidxauqdrexpqw66zlo3q6cmr2vuuvb3vletnugkf33uuxwt2um4ry" }, - { "/": "bafyreier7y2snffy7x75y4ri6ahiflxmz7ksklizzwpphwgpfotc3omo2y" } - ], - "bafyreidxauqdrexpqw66zlo3q6cmr2vuuvb3vletnugkf33uuxwt2um4ry": { - "v": "0.1.0", - "with": "https://example.com/report", - "do": "crud/update", - "input": { - "payload": { - "event": "email-notification", - "from": "mailto://alice@exmaple.com", - "to": ["bob@exmaple.com", "carol@example.com"] - }, - "_": [ - { - "await/ok": { - "ucan/task": { - "/": "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm" - } - } - }, - { - "await/ok": { - "ucan/task": { - "/": "bafyreifjjvdd6hoi7wjot7tjaubwztqldds332bgq6a4n37d6s5slxkbvy" - } - } - } - ] - }, - "prf": [ - { "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" } - ] + "auth": { + "/": "bafyreigjddazpbxmomcl32ryjxaxqymdrvzpqzjq5xtctdncn65kszmsoi" }, - "bafyreier7y2snffy7x75y4ri6ahiflxmz7ksklizzwpphwgpfotc3omo2y": { - "scope": [ - { "/": "bafyreigb54cv7jl4rt32nl5r7udzhbavd7c4ct5pkfkuept2ufrpig3zli" }, - { "/": "bafyreifjjvdd6hoi7wjot7tjaubwztqldds332bgq6a4n37d6s5slxkbvy" }, - { "/": "bafyreieahqhc2dwrnucyljhpqqsskqak2tc4ehhd6lvxpzuztx72k4xidm" }, - { "/": "bafyreidxauqdrexpqw66zlo3q6cmr2vuuvb3vletnugkf33uuxwt2um4ry" } - ], - "s": { - "/": { - "bytes": "7aEDQPdMfJiaNTzmTVHiJhkcX6mlKOG2piEk0OtpLslJeaimx4uM4/hGcadQ3Z6qhu2j761PW4RKyC1+BiWB+jO7LwA" - } + "prf": [ + { + "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" } - } + ] }, - "roots": [ - { "/": "bafyreiesse5saoa5dj7f5mh7sffy57vfhnjm6tpgwmwxe3ncwin2hwqsoy" }, - { "/": "bafyreif7cskac7ongewanqahk2vljkk5rsiblm2vqqo57j2d6mgeuhtuxq" }, - { "/": "bafyreieeipburgtzg4hr2ghxgspc53szrkstaz4syjat3tex75hjdrau3y" }, - { "/": "bafyreid5bb7z7l4ti57gdep6tbsnam47555ivnh3znpylo2n7qqyiiggqm" } - ] + "bafyreigjddazpbxmomcl32ryjxaxqymdrvzpqzjq5xtctdncn65kszmsoi": { + "scope": [ + { + "/": "bafyreihbli7vcw2n42xqv43ushojh7nvto6zpb3rd5ekoo6mim6bfkkqku" + }, + { + "/": "bafyreifiwxa2mnjvbihr45q56j6vyy4ksml7fh2tq2wnqtf5n55yveevja" + }, + { + "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" + }, + { + "/": "bafyreiail3bkoyow46d6gnisj4dttiitifiaodee3ixynbhyq6vzxnvj2q" + } + ], + "s": { + "/": { + "bytes": "7aEDQLbVVvN/RU8juyz+r36xMgCP1Eh1OknVckuCPrkTmvGS+ULTtCcvjF3gCqpqf6As7VLewoqTvWX1sswRudmOvAY" + } + } + } } ``` @@ -1424,164 +1307,128 @@ flowchart TB ```json { - "blocks": { - "bafyreifcerdvicarktlnif5uj25ultgpedwg63nxhmp7anoepaamqj4eji": { - "do": "crud/update", - "with": "dns:example.com?TYPE=TXT", - "input": { - "value": "hello world" - } - }, - "bafyreieogoqh5rolyt4livorznwhg63ml4oon3vdxnkqvc7svya4gisppe": { - "v": "0.1.0", - "run": { - "/": "bafyreifcerdvicarktlnif5uj25ultgpedwg63nxhmp7anoepaamqj4eji" - }, - "prf": [ - { - "/": "bafyreieynwqrabzdhgl652ftsk4mlphcj3bxchkj2aw5eb6dc2wxieilau" - } - ] - }, - "bafyreieqqhr7pznb77ffngpqnhmlcoi3666zc7qmhvdzs26oy6fclqp4dy": { - "do": "msg/send", - "with": "mailto://alice@example.com", - "input": { - "to": "bob@example.com", - "subject": "DNSLink for example.com", - "body": { - "await/ok": { - "/": "bafyreieogoqh5rolyt4livorznwhg63ml4oon3vdxnkqvc7svya4gisppe" - } + "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny": { + "on": "dns:example.com?TYPE=TXT", + "call": "crud/update", + "input": { + "value": "hello world" + } + }, + "bafyreihbli7vcw2n42xqv43ushojh7nvto6zpb3rd5ekoo6mim6bfkkqku": { + "on": "mailto://alice@example.com", + "call": "msg/send", + "input": { + "to": "bob@example.com", + "subject": "DNSLink for example.com", + "body": { + "await/ok": { + "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" } } + } + }, + "bafyreieeq5rf6uolaitbam2v56xmoz466eri3xcfwfawphon644cbgbkra": { + "v": "0.1.0", + "run": { + "/": "bafyreihbli7vcw2n42xqv43ushojh7nvto6zpb3rd5ekoo6mim6bfkkqku" }, - "bafyreid52dldaayikwgscn43o5gurlrujmlppdejductzdqnsvizvjplze": { - "v": "0.1.0", - "run": { - "/": "bafyreieqqhr7pznb77ffngpqnhmlcoi3666zc7qmhvdzs26oy6fclqp4dy" - }, - "auth": { - "/": "bafyreifxyud6cnw26g3t54xsjcjilu6j7laqlge6fds6rgcezl2waivvdi" - }, - "prf": [ - { - "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" - } - ] + "auth": { + "/": "bafyreif343mg6uhtqa7qbuvd3glu3vjaya5q757dqyobwt5wysbh4h5rnq" }, - "bafyreifxyud6cnw26g3t54xsjcjilu6j7laqlge6fds6rgcezl2waivvdi": { - "scope": [ - { - "/": "bafyreifcerdvicarktlnif5uj25ultgpedwg63nxhmp7anoepaamqj4eji" - }, - { - "/": "bafyreieqqhr7pznb77ffngpqnhmlcoi3666zc7qmhvdzs26oy6fclqp4dy" - } - ], - "s": { - "/": { - "bytes": "7aEDQFftPMepLGONVlthmxHaa7UbPnwVwJnDdIPrV/WcBTb1lot7OrEblJShcnrHrtcDNe8CRXQK9N58fPjYt4OQfQM" - } + "prf": [ + { + "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" } - } + ] }, - "roots": [ - { - "/": "bafyreid52dldaayikwgscn43o5gurlrujmlppdejductzdqnsvizvjplze" + "bafyreif343mg6uhtqa7qbuvd3glu3vjaya5q757dqyobwt5wysbh4h5rnq": { + "scope": [ + { + "/": "bafyreihbli7vcw2n42xqv43ushojh7nvto6zpb3rd5ekoo6mim6bfkkqku" + }, + { + "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" + } + ], + "s": { + "/": { + "bytes": "7aEDQG2GvLnr2gVEfMDrEUV8S3fw8JuFGVKAGIhSZCqCmHGyQ8cdU2A/Vp97yAsZQ+tqBaMWN3Q6YJLfPpAdgaXf2gY" + } } - ] + } } ``` ```json { - "blocks": { - "bafyreif2l5ykv6pbdxd636o2iyhr7evorygjczhbfrlhkr5t65t5xjjdm4": { - "do": "msg/send", - "with": "mailto://alice@example.com", - "input": { - "to": "carol@example.com", - "subject": "Hey Carol, DNSLink was updated!", - "body": { - "await/ok": { - "/": "bafyreieogoqh5rolyt4livorznwhg63ml4oon3vdxnkqvc7svya4gisppe" - } + "bafyreifiwxa2mnjvbihr45q56j6vyy4ksml7fh2tq2wnqtf5n55yveevja": { + "on": "mailto://alice@example.com", + "call": "msg/send", + "input": { + "to": "carol@example.com", + "subject": "Hey Carol, DNSLink was updated!", + "body": { + "await/ok": { + "/": "bafyreievhy7rnzot7mnzbnqtiajhxx7fyn7y2wkjtuzwtmnflty3767dny" } } - }, - "bafyreifwe7q42d54pxmdszzdcayagrkb4btsto5zcfwn434n7yq57v3mxe": { - "v": "0.1.0", - "run": { - "/": "bafyreif2l5ykv6pbdxd636o2iyhr7evorygjczhbfrlhkr5t65t5xjjdm4" + } + }, + "bafyreiail3bkoyow46d6gnisj4dttiitifiaodee3ixynbhyq6vzxnvj2q": { + "on": "https://example.com/report", + "call": "crud/update", + "input": { + "payload": { + "from": "mailto://alice@exmaple.com", + "to": [ + "bob@exmaple.com", + "carol@example.com" + ], + "event": "email-notification" }, - "prf": [ + "_": [ { - "/": "bafyreibblnq5bawcchzh73nxkdmkx47hu64uwistvg4kyvdgfd6igkcnha" - } - ] - }, - "bafyreieym67mo7s5wjgwcnxrnn75vn4nn5w33snicuc34ppp4t5dshphya": { - "do": "crud/update", - "with": "https://example.com/report", - "input": { - "payload": { - "from": "mailto://alice@exmaple.com", - "to": [ - "bob@exmaple.com", - "carol@example.com" - ], - "event": "email-notification" - }, - "_": [ - { - "await/ok": { - "/": "bafyreid52dldaayikwgscn43o5gurlrujmlppdejductzdqnsvizvjplze" - } - }, - { - "await/ok": { - "/": "bafyreifwe7q42d54pxmdszzdcayagrkb4btsto5zcfwn434n7yq57v3mxe" - } + "await/ok": { + "/": "bafyreihbli7vcw2n42xqv43ushojh7nvto6zpb3rd5ekoo6mim6bfkkqku" } - ] - } - }, - "bafyreiahe6v4vsr3gzqn6s6wxcuauyoqvuzh524tkxiwr4lzcskywsc6p4": { - "v": "0.1.0", - "run": { - "/": "bafyreieym67mo7s5wjgwcnxrnn75vn4nn5w33snicuc34ppp4t5dshphya" - }, - "auth": { - "/": "bafyreih6b5uc3xzhh6xnta7iqbwhe5t62kekzcjpssx5adyh5pky7m5j2e" - }, - "prf": [ + }, { - "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" + "await/ok": { + "/": "bafyreifiwxa2mnjvbihr45q56j6vyy4ksml7fh2tq2wnqtf5n55yveevja" + } } ] + } + }, + "bafyreih6wl2uvxuvpeicddv7exg5nixvc7kydbj3wlq24z37q5rd6vo5ky": { + "v": "0.1.0", + "run": { + "/": "bafyreiail3bkoyow46d6gnisj4dttiitifiaodee3ixynbhyq6vzxnvj2q" }, - "bafyreih6b5uc3xzhh6xnta7iqbwhe5t62kekzcjpssx5adyh5pky7m5j2e": { - "scope": [ - { - "/": "bafyreif2l5ykv6pbdxd636o2iyhr7evorygjczhbfrlhkr5t65t5xjjdm4" - }, - { - "/": "bafyreieym67mo7s5wjgwcnxrnn75vn4nn5w33snicuc34ppp4t5dshphya" - } - ], - "s": { - "/": { - "bytes": "7aEDQKkxnSFpFpj51Q715C7FWemiXgSKXdHZyN0XBsOym+/yBaF46iX98XYF789cdkRBQACUfzM5cRhFYvwZpljitA8" - } + "auth": { + "/": "bafyreiersek7u2lvzyolszwm5j5iy74qshc4oekszao7dmbwnnx4623du4" + }, + "prf": [ + { + "/": "bafyreiflsrhtwctat4gulwg5g55evudlrnsqa2etnorzrn2tsl2kv2in5i" } - } + ] }, - "roots": [ - { - "/": "bafyreiahe6v4vsr3gzqn6s6wxcuauyoqvuzh524tkxiwr4lzcskywsc6p4" + "bafyreiersek7u2lvzyolszwm5j5iy74qshc4oekszao7dmbwnnx4623du4": { + "scope": [ + { + "/": "bafyreifiwxa2mnjvbihr45q56j6vyy4ksml7fh2tq2wnqtf5n55yveevja" + }, + { + "/": "bafyreiail3bkoyow46d6gnisj4dttiitifiaodee3ixynbhyq6vzxnvj2q" + } + ], + "s": { + "/": { + "bytes": "7aEDQM1yNTEO/+TF69wUwteH+ftAjD0ik5tXDa+sheAiuOZobSco/+vU882/Nf3LtMRF1EDoP/H38PX2bD5nJzkHAAU" + } } - ] + } } ``` @@ -1633,5 +1480,4 @@ Thanks to [Rod Vagg](https://github.com/rvagg/) for the clarifications on IPLD S [effect]: #7-effect [receipt]: #8-receipt [pipelines]: #9-pipelines -[promise]: #92-promise -[await]: #93-await +[await]: #await From 986acf17b24fbe5b5814b3b9338d2ac71274a73a Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Fri, 10 Feb 2023 15:45:43 -0800 Subject: [PATCH 151/154] consent to license --- Notices.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Notices.md b/Notices.md index 17a19798..0aac6d84 100644 --- a/Notices.md +++ b/Notices.md @@ -22,6 +22,14 @@ Specification version: 0.1.0 or later --------------------------------------------------------------------------------- +Licensee’s name: Irakli Gozalishvili + +Authorized individual and system identifier: gozala + +Specification version: 0.1.0 or later + +--------------------------------------------------------------------------------- + ## Withdrawals Name of party withdrawing: From 512bf889f71ec072bdd41bfeaa06459d923bfb4e Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Fri, 10 Feb 2023 15:50:05 -0800 Subject: [PATCH 152/154] Apply suggestions from code review Signed-off-by: Irakli Gozalishvili --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9345e7c5..1265a6c2 100644 --- a/README.md +++ b/README.md @@ -762,7 +762,7 @@ The result of an [Invocation] MAY include a request for further actions to be pe 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`. -[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 guarintees such as failure of one effect impling failure of other effects if left undefined. +[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. @@ -1108,7 +1108,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 substituded with `Await`. +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`. 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). From 882ca8fa1aabb25f9cd277eaf28e5ca27a2b68f0 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sun, 12 Feb 2023 15:44:08 -0800 Subject: [PATCH 153/154] fix typo caught by @jchris Signed-off-by: Irakli Gozalishvili --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1265a6c2..55177826 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ UCAN delegation can be gossiped freely between services. This is not true for in 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 this 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. +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 From 6613c31eb0dd97ee9ee85a68f0d01b84c57616fd Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sun, 12 Feb 2023 15:48:08 -0800 Subject: [PATCH 154/154] Fix spelling mistakes Signed-off-by: Irakli Gozalishvili --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 55177826..8976c2bb 100644 --- a/README.md +++ b/README.md @@ -758,7 +758,7 @@ If no information is available, this field SHOULD be set to `{}`. ## 7 Effect -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 reuqesting 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. 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`. @@ -900,7 +900,7 @@ If `iss` field is omitted, Receipt MUST be signed by the [Executor]. ### 8.2.6 Proofs -If OPTIONAL `prf` field is present, MUST contain link to UCAN delegatation authorizing Receipt Issuer (`iss`) to carry [Task] execution. +If OPTIONAL `prf` field is present, MUST contain link to UCAN delegation authorizing Receipt Issuer (`iss`) to carry [Task] execution. ### 8.2.7 Signature