Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.0.0-rc.1: High level invocation spec #21

Open
wants to merge 115 commits into
base: main
Choose a base branch
from
Open

v1.0.0-rc.1: High level invocation spec #21

wants to merge 115 commits into from

Conversation

expede
Copy link
Member

@expede expede commented Aug 21, 2023

Preview 📜

TODOs

Copy link
Contributor

@Gozala Gozala left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provided some feedback and a lot of questions

README.md Outdated
# 3 Task
| Name | Field | Type | Required |
|-------------|-------|------------------|----------|
| [Subject] | `sub` | `DID` | No |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If subject is omitted are proofs still required ? If so how do subjects on them affect the invocation ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, perhaps this is confusing. You need to align the delegation.sub with the invoctaion.aud. I see why that may not be ideal.

README.md Outdated
| Name | Field | Type | Required |
|-------------|-------|------------------|----------|
| [Subject] | `sub` | `DID` | No |
| [Command] | `cmd` | `String` | Yes |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Could we replace cmd string with a link to a UCAN been invoked ? Given UCANs now only hold single capability it becomes clear what command is been invoked.

I kind of like that as UCAN becomes basically essentially a reference (to a function and imposed caveats it's type signature)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm yeah now that we have a single resource per invocation, this is a thing we could do 🤔 I guess it doesn't work if your proof chain starts with a ucan/* or equivalent (to connect disparate chains). Will think on it, but really we're talking about keeping or eliminating some redundancy.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm you know, sub now imposes a context (in the W3C/RDF sense) for the cmd and args... mayeb it should always be required as the authority. So things like Wasm (which can be run by anyone) would just have a standard sub set by the UCAN WG.

README.md Outdated
Comment on lines 257 to 260
The OPTIONAL `sub` field is intended for cases where parameterizing a specific agent is important. This is especially critical for two parts of the life cycle:

The 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.
1. Specifying a particular `sub` (and thus `aud`) when [enqueuing new Tasks][enqueue] in a Receipt
2. Indexing Receipts for reverse lookup and memoization
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I understand what is the subject field for. I was under impression it was made optional to allow permissionless invocations. I am really not sure why sub would impose desired aud of the enqueued task. I also don't follow the receipt reverse lookup note here.

README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated
| `ucr` | `SemVer` | Yes | The version of this spec that the Receipt conforms to |
| `iss` | `DID` | Yes | The DID of the Executor |
| `ran` | `&Invocation` | Yes | A link to the [Invocation] that the Receipt is for |
| `prf` | `[&Delegation]` | Yes | [Delegation] proof chain if the Executor was not the `aud` of the `ran` Invocation |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we have implemented revocations as a ucan/revoke capability I had been wondering if same makes sense for the receipts. Specifically this proof chain had been motivated by the fact that we want a delegate of the "Executor" to run a task and then issue receipt on their behalf.

If we were able to represent this as a capability e.g. ucan/conclude (or something along those lines) we would naturally obtain an to issue receipts on behalf of "Executor" without additional fields like this.

With such a setup Executor could delegate

{
  iss: "did:web:web3.storage"
  aud: "did:key:zWorker",
  sub: "did:web:web3.storage",
  can: "ucan/conclude"
  // ...
}

Then above ☝️ delegation will allow did:key:zWorker to issue receipt in form of invocation back to the invoker

{
  "sig": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}},
  "inv": cid({
    "iss": "did:key:zWorker",
    "aud": "did:key:zInvoker",
    "run": cid({
      "act": {
        "nnc": "",
        "cmd": "ucan/conclude",
        "arg": {
          "ran": {"/": "bafyreia5tctxekbm5bmuf6tsvragyvjdiiceg5q6wghfjiqczcuevmdqcu"},
          "out": {
             "ok": ["bob@example.com", "alice@example.com"]
           },
        }
      }
    }),
    "prf": [
       cid({
         sig: { "/": { bytes: "...." },
         ucd: cid({
           iss: "did:web:web3.storage",
           aud: "did:key:zWorker",
           sub: "did:web:web3.storage",
           can: "ucan/conclude"
           // ...
         })
       })
      ],
      "exp": null
  })
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting! Let me mull that over

README.md Outdated
| `out` | `Result` | Yes | The value output of the invocation in [Result] format |
| `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue |
| `mta` | `{String : Any}` | Yes | Additional data about the receipt |
| `rec` | `&Receipt` | No | Recursive `Receipt`s if the Invocation was proxied to another Executor |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps if we were to make receipt a capability (as per comment above) it would also address this use case as the proxy executors would simply be able to re-delegate receipt and consequently capture it in proof chain as opposed to another custom field.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡

README.md Outdated
| `enq` | `[&Task]` | Yes | Further [Task]s that the [Invocation] would like to enqueue |
| `mta` | `{String : Any}` | Yes | Additional data about the receipt |
| `rec` | `&Receipt` | No | Recursive `Receipt`s if the Invocation was proxied to another Executor |
| `iat` | `Integer`[^js-num] | No | The UTC Unix timestamp at which the Receipt was issued |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is cool, in fact we have recently started putting timestamps in all our Result values as we wanted to capture time they were issued.


### 8.2.6 Proofs
Enqueued [Task]s describe requests for future work to be performed. They SHOULD come with [Delegation]s, but MAY be a simple request back to the Invoker.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but MAY be a simple request back to the Invoker.

Can you please unpack this a bit more as it's not obvious to me what "simple request" implies here.

Also if I recall correctly in v0.2 you wanted effects to reference Instruction (a.k.a Action here) as opposed to Task I think so that you won't have to specify proofs. I don't mind going back on to linking to Task's, it actually simplifies things, but I do want to make sure this is conscious decision.


If OPTIONAL `prf` field is present, MUST contain link to UCAN delegation authorizing Receipt Issuer (`iss`) to carry [Task] execution.
All [Task]s in an [enqueue] array MUST be treated as concurrent, unless explicit data dependencies between them exist via [UCAN Promise]s.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not super happy that we lost a way to denote which task is a logical continuation of the running workflow. We could signal it differently e.g. last or a first element of the array could be treated as "join" but that is far less obvious then designated field. Furthermore it prevents us from expressing a case where we only fork bunch of tasks and terminate without a join.

Different names are fine, but here we lost something that some of our work started to depend on.

For what it's worth join was analogy to a thread.join while fork was analogy to a thread.spawn it's just fork was shorter and made more sense for multiple tasks as opposed to one.

Copy link
Member Author

@expede expede Nov 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For what it's worth join was analogy to a thread.join

Right, but that's not how join works. You don't chain joins together to continue computation. The analogy seems like a stretch to me.

but here we lost something that some of our work started to depend on.

We can add back in a special field (e.g. continuation) if there's a clear use case, but can you explain why you can't use the 0-index of the array.

At least structurally, these are equivalent:

data Fx = [Task]

data Fx' = Fx' {
  cont :: Maybe Task
  fork :: [Task]
}

toFx :: Fx' -> Fx
toFx Fx' {cont = Nothing, fork = fork} = Fx fork
toFx Fx' {cont = Just t,  fork = fork} = Fx (t : fork)

toFx' :: Fx -> Fx'
toFx' []       = Fx' {cont = Nothing, fork = []}
toFx' (t : ts) = Fx' {cont = Just t,  fork = ts}

Since this is structured more like actors or dataflow, there isn't really a "main" thread, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't chain joins together to continue computation.

That is precisely what we do in workflows that process data through multiple queues. We usually have piece/offer task that will have .join on piece/accept which in turn will have join on aggregate/offer etc...

can you explain why you can't use the 0-index of the array.

If it's an array with one element does it imply that it is a continuation or concurrent task ? Whatever's answer how do I express case where I have not both but the other from one you've answered.

In other words we wan to express tree different cases {fork}, {join} and {fork, join} with [Task, ...Task[]] we only get [Join] or [Join, ...Fork[]] and can not get [...Fork[]] unless we add another signal (which is what the current structure did)


I suspect for IPVM join is essentially inferred from await/...'s, which makes sense. However in our experience it is a lot easier to UCAN-ify legacy systems if you can simply express join's without having to invert the control flow.

README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
expede and others added 5 commits October 19, 2023 18:30
Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com>
Signed-off-by: Brooklyn Zelenka <be.zelenka@gmail.com>
Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com>
Signed-off-by: Brooklyn Zelenka <be.zelenka@gmail.com>
Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com>
Signed-off-by: Brooklyn Zelenka <be.zelenka@gmail.com>

Note over 🚗, Boris: Akiko asks Boris to use her car to run errands
Akiko ->> Boris: Invoke!(Boris to run errands, using 🚗 (➌))
Boris ->> 🚗: Invoke!(Drive 🚗)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Boris ->> 🚗: Invoke!(Drive 🚗)
Boris ->> 🚗: Execute!(➍)

In the example above, steps ➌ and ➍ are qualitatively different:

- Step ➌ grants authority (to drive the car)
- Step ➍ is a _command_ to do so
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Step ➍ is a _command_ to do so
- Step ➍ is a _request_ to do so

To not have name collision with Command of this spec

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe

  • Step ➌ authorizes capability (to drive the car)
  • Step ➍ requests to excercise / perform it

@expede expede marked this pull request as ready for review December 20, 2023 15:49
"do": "msg/send",
"args": {
"from": "mailto:alice@example.com",
"to": [ "bob@example.com", "carol@example.com" ],
"subject": "hello",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed on today's call-- someone CTRL-Fing for subject could get tripped up, so a quick rename to emailSubject or messageTitle or same might be a quick fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants