9. Vanes

In this lesson we're going to look at interacting with vanes (kernel modules). The API for each vane consists of tasks it can take, and gifts it can return. The tasks and gifts for each vane are defined in its section of /sys/lull.hoon. Here's the $task:iriss and $gift:iriss for Iris, the HTTP client vane, as an example:

|%
+$  gift
  $%  [%request id=@ud request=request:http]
      [%cancel-request id=@ud]
      [%http-response =client-response]
  ==
+$  task
  $~  [%vega ~]
  $%  $>(%born vane-task)
      $>(%trim vane-task)
      $>(%vega vane-task)
      [%request =request:http =outbound-config]
      [%cancel-request ~]
      [%receive id=@ud =http-event:http]
  ==

The API of each vane is documented in its respective section of the Arvo documentation. Each vane has a detailed API reference and examples of their usage. There are far too many tasks and gifts across the vanes to cover here, so in the Example section of this document, we'll just look at a single, simple example with a Behn timer. The basic pattern in the example is broadly applicable to the other vanes as well.

Sending a vane task

A task can be sent to a vane by %passing it an %arvo card. We touched on these in the Cards lesson, but we'll briefly recap it here. The type of the card is as follows:

[%pass path %arvo note-arvo]

The $path will just be the $wire you want the response to arrive on. The $note-arvo is the following union:

+$  note-arvo
  $~  [%b %wake ~]
  $%  [%a task:ames]
      [%b task:behn]
      [%c task:clay]
      [%d task:dill]
      [%e task:eyre]
      [%g task:gall]
      [%i task:iris]
      [%j task:jael]
      [%k task:khan]
      [%l task:lick]
      [%$ %whiz ~]
      [@tas %meta vase]
  ==

The letter tags just specify which vane it goes to, and then follows the task itself. Here are a couple of examples. The first sends a %wait $task:behn to Behn, setting a timer to go off one minute in the future. The second sends a %warp $task:clay to Clay, asking whether sys.kelvin exists on the %base desk.

[%pass /some/wire %arvo %b %wait (add ~m1 now.bowl)]
[%pass /some/wire %arvo %c %warp our.bowl %base ~ %sing %u da+now.bowl /sys/kelvin]

Receiving a vane gift

Once a task has been sent to a vane, any gifts the vane sends back in response will arrive in the +on-arvo arm of your agent. The +on-arvo arm exclusively handles such vane gifts. The gifts will arrive in a $sign-arvo, along with the $wire specified in the original request. The +on-arvo arm produces a (quip card _this) like usual, so it would look like:

++  on-arvo
  |=  [=wire =sign-arvo]
  ^-  (quip card _this)
  .....

A $sign-arvo is the following structure, defined in lull.hoon:

+$  sign-arvo
  $%  [%ames gift:ames]
      $:  %behn
          $%  gift:behn
              $>(%wris gift:clay)
              $>(%writ gift:clay)
              $>(%mere gift:clay)
              $>(%unto gift:gall)
          ==
      ==
      [%clay gift:clay]
      [%dill gift:dill]
      [%eyre gift:eyre]
      [%gall gift:gall]
      [%iris gift:iris]
      [%jael gift:jael]
      [%khan gift:khan]
      [%lick gift:lick]
  ==

The head of the $sign-arvo will be the name of the vane like %behn, %clay, etc. The tail will be the gift itself. Here are a couple of $sign-arvo examples, and the responses to the example tasks in the previous section:

[%behn %wake ~]
[ %clay
  [ %writ
      p
    [ ~
      [ p=[p=%u q=[%da p=~2021.11.17..13.55.00..c195] r=%base]
        q=/sys/kelvin
        r=[p=%flag q=[#t/?(%.y %.n) q=0]]
      ]
    ]
  ]
]

The typical pattern is to first test the $wire with something like a wutlus (?+) expression, and then test the $sign-arvo. Since most gifts are head-tagged, you can test both the vane and the gift at the same time like:

?+    sign-arvo  (on-arvo:def wire sign-arvo)
    [%behn %wake *]
  .....
....

Example

Here's a very simple example that takes a poke of a @dr (a relative date-time value) and sends Behn a %wait $task:behn, setting a timer to go off @dr in the future. When the timer goes off, +on-arvo will take the %wake $gift:behn and print "Ding!" to the terminal.

/app/ding.hoon
/+  default-agent, dbug
|%
+$  card  card:agent:gall
--
%-  agent:dbug
^-  agent:gall
|_  =bowl:gall
+*  this  .
    def   ~(. (default-agent this %.n) bowl)
++  on-init  on-init:def
++  on-save  on-save:def
++  on-load  on-load:def
++  on-poke
  |=  [=mark =vase]
  ^-  (quip card _this)
  ?+    mark  (on-poke:def mark vase)
      %noun
    :_  this
    :~  [%pass /timers %arvo %b %wait (add now.bowl !<(@dr vase))]
    ==
  ==
++  on-watch  on-watch:def
++  on-leave  on-leave:def
++  on-peek   on-peek:def
++  on-agent  on-agent:def
++  on-arvo
  |=  [=wire =sign-arvo]
  ^-  (quip card _this)
  ?+    wire  (on-arvo:def wire sign-arvo)
      [%timers ~]
    ?+    sign-arvo  (on-arvo:def wire sign-arvo)
        [%behn %wake *]
      ?~  error.sign-arvo
        ((slog 'Ding!' ~) `this)
      (on-arvo:def wire sign-arvo)
    ==
  ==
++  on-fail   on-fail:def
--

Let's examine the +on-poke arm:

++  on-poke
  |=  [=mark =vase]
  ^-  (quip card _this)
  ?+    mark  (on-poke:def mark vase)
      %noun
    :_  this
    :~  [%pass /timers %arvo %b %wait (add now.bowl !<(@dr vase))]
    ==
  ==

A Behn %wait task has the format [%wait @da] - the @da (an absolute date-time value) is the time the timer should go off. The $vase of the poke takes a @dr, so we extract it directly into an +add expression, producing a date-time @dr from now. Behn will receive the %wait task and set the timer in Unix. When it fires, Behn will produce a %wake $gift:behn and deliver it to +on-arvo, on the $wire we specified (/timers). Here's the +on-arvo arm:

++  on-arvo
  |=  [=wire =sign-arvo]
  ^-  (quip card _this)
  ?+    wire  (on-arvo:def wire sign-arvo)
      [%timers ~]
    ?+    sign-arvo  (on-arvo:def wire sign-arvo)
        [%behn %wake *]
      ?~  error.sign-arvo
        ((slog 'Ding!' ~) `this)
      (on-arvo:def wire sign-arvo)
    ==
  ==

We remark that, just like in the case of agent-agent communication, gifts from Arvo are also routed $wire before $sign-arvo.

First we check the $wire is /timers, and then we check the $sign-arvo begins with [%behn %wake ....]. Behn's %wake gift has the following format:

[%wake error=(unit tang)]

The .error is null if the timer fired successfully, and contains an error in the $tang if it did not. We therefore test whether .error.sign-arvo is ~, and if it is, we print Ding! to the terminal. If the $wire, $sign-arvo or .error are something unexpected, we pass it to %default-agent, which will just crash and print an error message.

Let's try it out. Save the agent above as /app/ding.hoon on the %base desk and |commit %base. Then, start the agent with |rein %base [& %ding].

Next, in the Dojo let's try poking our agent, setting a timer for five seconds from now:

> :ding ~s5
>=

After approximately five seconds, we see the timer fired successfully:

> Ding!

Summary

  • Each vane has an API composed of tasks it takes and gifts it produces.

  • Each vane's tasks and gifts are defined in lull.hoon

  • Each vane's section of the Arvo documentation includes an API reference that explains its tasks and gifts, as well as an Examples section demonstrating their usage.

  • Vane tasks can be sent to vanes by %passing them an %arvo $card.

  • Vane gifts come back to the +on-arvo arm of the agent core in a $sign-arvo.

Exercises

  • Run through the Example yourself if you've not done so already.

  • Have a look at some vane sections of lull.hoon to familiarize yourself with its structure.

  • Have a quick look at the API reference sections of a couple of vanes in the Arvo documentation.

Last updated