@@ -0,0 +1,834 @@
1++++
2+title = "8. Subscriptions"
3+weight = 40
4+template = "doc.html"
5++++
6+
7+In this lesson we're going to look at subscriptions. Subscriptions are probably
8+the most complicated part of writing agents, so there's a fair bit to cover.
9+
10+## Incoming subscriptions
11+
12+Subscription requests from other entities arrive in your agent's `on-watch` arm.
13+The `on-watch` arm takes the `path` to which they're subscribing, and produces a
14+`(quip card _this)`:
15+
16+```hoon
17+++ on-watch
18+ |= =path
19+ ^- (quip card _this)
20+ ...
21+```
22+
23+Your agent's subscription paths would be defined in this arm, typically in a
24+wutlus (`?+`) expression or similar:
25+
26+```hoon
27+?+ path (on-watch:def path)
28+ [%updates ~]
29+ ......
30+ ......
31+ [%blah %blah ~]
32+ ......
33+ ......
34+ [%foo @ ~]
35+ =/ when=@da (slav %da i.t.path)
36+ ......
37+ ......
38+ [%bar %baz *]
39+ ?+ t.t.path (on-watch:def path)
40+ ~ .....
41+ [%abc %def ~] .....
42+ [%blah ~] .....
43+ ==
44+==
45+```
46+
47+Subscription paths can be simple and fixed like the first two examples above:
48+`/updates` and `/blah/blah`. They can also contain "wildcard" elements, with an
49+atom of a particular aura encoded in an element of the `path`, as in the `[%foo @ ~]` example. The type pattern matcher is quite limited, so we just specify
50+such variable elements as `@`, and then decode them with something like `(slav %da i.t.path)` (for a `@da`), as in the example. The incoming `path` in this
51+example would look like `/foo/~2021.11.14..13.30.39..6b17`. For more information
52+on decoding atoms in strings, see the [Strings
53+Guide](/docs/hoon/guides/strings#decoding-from-text).
54+
55+In the last case of `[%bar baz *]`, we're allowing a variable number of elements
56+in the path. First we check it's `/foo/bar/...something...`, and then we check
57+what the "something" is in another wutlus expression and handle it
58+appropriately. In this case, it could be `/foo/bar`, `/foo/bar/abc/def`, or
59+`/foo/bar/blah`. You could of course also have "wildcard" elements here too, so
60+there's not really a limit to the complexity of your subscription paths, or the
61+data that might be encoded therein.
62+
63+Permissions can be checked as described in the previous lesson, comparing the
64+source `@p` of the request in `src.bowl` to `our.bowl` or any other logic you
65+find appropriate.
66+
67+If a permission check fails, the path is not valid, or any other reason you want
68+to reject the subscription request, your agent can simply crash. The behavior
69+here is the same as with `on-poke` - Gall will send a `%watch-ack` card in
70+response, which is either an ack (positive acknowledgement) or a nack (negative
71+acknowledgement). The `(unit tang)` in the `%watch-ack` will be null if
72+processing succeeded, and non-null if it crashed, with a stack trace in the
73+`tang`. Like with `poke-ack`s, you don't need to explicitly send a
74+`%watch-ack` - Gall will do it implicitly.
75+
76+As well as sending a `%watch-ack`, Gall will also record the subscription in the
77+`sup` field of the `bowl`, if it succeeded. Then, when you send updates out to
78+subscribers of the `path` in question, the new subscriber will begin receiving
79+them as well.
80+
81+Updates to subscribers would usually be sent from other arms, but there's one
82+special case for `on-watch` which is very useful. Normally updates can only be
83+sent to all subscribers of a particular path - you can't target a specific
84+subscriber. There's one exception to this: In `on-watch`, when there's a new
85+subscription, you can send a `%fact` back with a null `(list path)`, and it'll
86+only go to the new subscriber. This is most useful when you want to give the
87+subscriber some initial state, which you otherwise couldn't do without sending
88+it to everyone. It might look something like this:
89+
90+```hoon
91+:_ this
92+:~ [%give %fact ~ %todo-update !>(`update:todo`initial+tasks)]
93+==
94+```
95+
96+## Sending updates to subscribers
97+
98+Once your agent has subscribers, it's easy to send them out updates. All you
99+need to do is produce `card`s with `%fact`s in them:
100+
101+```hoon
102+:_ this
103+:~ [%give %fact ~[/some/path /another/path] %some-mark !>('some data')]
104+ [%give %fact ~[/some/path] %some-mark !>('more data')]
105+ ....
106+==
107+```
108+
109+The `(list path)` in the `%fact` specifies which subscription `path`s to which
110+the `%fact` should be sent. All subscribers of all `path`s specified will
111+receive the `%fact`. Any agent arm which produces a `(quip card _this)` can send
112+`%fact`s to subscribers. Most often they will be produced in the `on-poke` arm,
113+since new data will often be added in `poke`s.
114+
115+## Kicking subscribers
116+
117+To kick a subscriber, you just send a `%kick` `card`:
118+
119+```hoon
120+[%give %kick ~[/some/path] `~sampel-palnet]
121+```
122+
123+The `(list path)` specifies which subscription `path`s the ship should be kicked
124+from, and the `(unit ship)` specifies which ship to kick. The `(unit ship)` can also be null, like so:
125+
126+```hoon
127+[%give %kick ~[/some/path] ~]
128+```
129+
130+In this case, all subscribers to the specified `path`s will be kicked.
131+
132+Note that `%kick`s are not exclusively sent by the agent itself - Gall can also
133+automatically kick subscribers under certain network conditions. Because of
134+this, `%kick`s are not typically interpreted as intentional, and the usual
135+behavior for a kicked agent is to try and resubscribe. Therefore, if you want to
136+disallow a particular subscriber, your agent's `on-watch` arm should reject
137+further subscription requests from them - your agent should not just `%kick`
138+them and call it a day.
139+
140+## Outgoing subscriptions
141+
142+Now that we've covered incoming subscriptions, we'll look at the other side of
143+it: Subscribing to other agents. This is done by `%pass`ing the target agent a
144+`%watch` task in a `card`:
145+
146+```hoon
147+[%pass /some/wire %agent [~some-ship %some-agent] %watch /some/path]
148+```
149+
150+If yor agent's subscription request is successful, updates will come in to your
151+agent's `on-agent` arm on the `wire` (`/some/wire` in this example) specified.
152+The `wire` can be anything you like - its purpose is for your agent to figure
153+out which subscription the updates are for. The `[ship term]` pair specifies the
154+ship and agent you're trying to subscribe to, and the final `path` (`/some/path`
155+in this example) is the subscription path to which you want to to subscribe - a
156+`path` the target agent has defined in its `on-watch` arm.
157+
158+Gall will deliver the `card` to the target agent and call that agent's
159+`on-watch` arm, which will process the request [as described
160+above](#incoming-subscription-requests), accept or reject it, and send back
161+either a positive or negative `%watch-ack`. The `%watch-ack` will come back in to your agent's `on-agent` arm in a `sign`, along with the `wire` you specified (`/some/wire` in this example):
162+
163+```hoon
164+++ on-agent
165+ |= [=wire =sign:agent:gall]
166+ ^- (quip card _this)
167+ .....
168+```
169+
170+The `sign` will be of the following format:
171+
172+```hoon
173+[%watch-ack p=(unit tang)]
174+```
175+
176+How you want to handle the `%watch-ack` really depends on the particular agent. In the simplest case, you can just pass it to the `on-agent` arm of `%default-agent`:
177+
178+```hoon
179+++ on-agent on-agent:def
180+```
181+
182+`%default-agent` will just accept it and do nothing apart from printing the
183+error in the `%watch-ack` `tang` if it's a nack. You shouldn't have your agent
184+crash on a `%watch-ack` - even if it's a nack your agent should process it
185+successfully. If you wanted to apply some additional logic on receipt of the
186+`%watch-ack`, you'd typically first test the `wire`, then test whether it's a
187+`%watch-ack`, then test whether it's an ack or a nack and do whatever's
188+appropriate:
189+
190+```hoon
191+++ on-agent
192+ |= [=wire =sign:agent:gall]
193+ ^- (quip card _this)
194+ ?+ wire (on-watch:def wire sign)
195+ [%expected %wire ~]
196+ ?+ -.sign (on-watch:def wire sign)
197+ %watch-ack
198+ ?~ p.sign
199+ ...(do something if ack)...
200+ ...(do something if nack)...
201+ ......
202+```
203+
204+The `on-agent` arm produces a `(quip card _this)`, so you can produce new
205+`card`s and update your agent's state, as appropriate.
206+
207+One further thing to note with subscriptions is that you can subscribe multiple
208+times to the same `path` on the same ship and agent, as long as the `wire` is
209+unique. If the ship, agent, `path` and `wire` are all the same as an existing
210+subscription, Gall will not allow the request to be sent, and instead fail with
211+an error message fed into the `on-fail` arm of your agent.
212+
213+## Receiving updates
214+
215+Assuming the `%watch` succeeded, your agent will now begin receiving any
216+`%fact`s the agent to which you've subscribed publishes on the path to which
217+you've subscribed. These `%fact`s will also come in to your agent's `on-agent`
218+arm in a `sign`, just like the initial `%watch-ack`. The `%fact` `sign` will
219+have the following format:
220+
221+```hoon
222+[%fact =cage]
223+```
224+
225+You would typically handle such `%fact`s in the following manner: Test the
226+`wire`, test whether the `sign` is a `%fact`, test the `mark` in the `cage`,
227+extract the data from the `vase` in the `cage`, and apply your logic. For example:
228+
229+```hoon
230+++ on-agent
231+ |= [=wire =sign:agent:gall]
232+ ^- (quip card _this)
233+ ?+ wire (on-watch:def wire sign)
234+ [%expected %wire ~]
235+ ?+ -.sign (on-watch:def wire sign)
236+ %fact
237+ ?+ p.cage.sign (on-watch:def wire sign)
238+ %expected-mark
239+ =/ foo !<(expected-type q.cage.sign)
240+ .....
241+ ......
242+```
243+
244+Note that Gall will not allow `sign`s to come into `on-agent` unsolicited, so
245+you don't necessarily need to include permission logic in this arm.
246+
247+The `on-agent` arm produces a `(quip card _this)`, so you can produce new
248+`card`s and update your agent's state, as appropriate.
249+
250+## Getting kicked
251+
252+For whatever reason, the agent you're `%watch`ing might what to kick your agent
253+from a `path` to which it's suscribed, ending your subscription and ceasing to
254+send your agent `%fact`s. To do this, it will send your agent a `%kick` card [as
255+described above](#kicking-subscribers). The `%kick` will come in to your agent's
256+`on-agent` arm in a `sign`, like `%watch-ack`s and `%fact`s do. The `%kick`
257+`sign` will have the following format:
258+
259+```hoon
260+[%kick ~]
261+```
262+
263+Since the `%kick` itself contains no information, you'll need to consider the
264+`wire` it comes in on to know what it pertains to. As explained previously,
265+`%kick`s aren't always intentional - sometimes Gall will kick subscribers due to
266+network issues. Your `on-agent` arm therefore has no way to know whether the
267+other agent actually intended to kick it. This means _your agent should almost
268+always try to resubscribe if it gets kicked_. Then, if the resubscribe `%watch`
269+request is rejected with a negative `%watch-ack`, you can conclude that it was
270+intentional and give up. The logic would look something like this:
271+
272+```hoon
273+++ on-agent
274+ |= [=wire =sign:agent:gall]
275+ ^- (quip card _this)
276+ ?+ wire (on-agent:def wire sign)
277+ [%some %wire ~]
278+ ?+ -.sign (on-agent:def wire sign)
279+ %kick
280+ :_ this
281+ :~ [%pass /some/wire %agent [src.bowl dap.bowl] %watch /some/path]
282+ ==
283+ .......
284+```
285+
286+## Leaving a subscription
287+
288+Eventually you may wish to unsubscribe from a `path` in another agent and stop
289+receiving updates. This is done by `%pass`ing a `%leave` task to the agent in
290+question:
291+
292+```hoon
293+[%pass /some/wire %agent [~some-ship %some-agent] %leave ~]
294+```
295+
296+The subcription to be ended is determined by the combination of the `wire`, ship
297+and agent, so the `%leave` task itself always just has `~` at the end.
298+
299+## Example
300+
301+Here we're going to give a pretty well fleshed out example. It will demonstrate
302+both inbound and outbound subscriptions, most of the concepts we've discussed
303+here, as well as some from the previous lesson - `/sur` files, `mark` files, and
304+permission checks.
305+
306+In previous lessons we've only dealt with things on a local ship - this example
307+will demonstrate messages being sent over the network.
308+
309+The example will be composed of two separate agents - a publisher called
310+`/app/todo.hoon` and a subscriber called `/app/todo-watcher.hoon`, which will
311+live on separate ships. It will be a very rudimentary To-Do app - to-do tasks
312+will be poked into the publisher and sent out to the subscriber as `%fact`s,
313+which will just print them to the dojo. It will have its types defined in
314+`/sur/todo.hoon`, and it will have a couple of `mark` files for pokes and
315+updates: `/mar/todo/action.hoon` and `/mar/todo/update.hoon`.
316+
317+Before we get into trying it out, we'll first walk through the `/sur` file, mark
318+files, and each agent.
319+
320+### Types and marks
321+
322+#### `/sur/todo.hoon`
323+
324+```hoon
325+|%
326++$ id @
327++$ name @t
328++$ task [=name done=?]
329++$ tasks (map id task)
330++$ who @p
331++$ friends (set who)
332++$ action
333+ $% [%add =name]
334+ [%del =id]
335+ [%toggle =id]
336+ [%rename =id =name]
337+ [%allow =who]
338+ [%kick =who]
339+ ==
340++$ update
341+ $% [%add =id =name]
342+ [%del =id]
343+ [%toggle =id]
344+ [%rename =id =name]
345+ [%initial =tasks]
346+ ==
347+--
348+```
349+
350+This file defines most of the types for the agents. The list of to-do tasks will
351+be stored in the state of the publisher agent as the `tasks` type, a `(map id task)`, where a `task` is a `[=name done=?]`. The set of ships allowed to
352+subscribe will be stored in `friends`, a `(set @p)`, also in the publisher's
353+state. After that, there are the head-tagged unions of accepted poke `action`s
354+and `update`s for subscribers.
355+
356+#### `/mar/todo/action.hoon`
357+
358+```hoon
359+/- todo
360+|_ =action:todo
361+++ grab
362+ |%
363+ ++ noun action:todo
364+ --
365+++ grow
366+ |%
367+ ++ noun action
368+ --
369+++ grad %noun
370+--
371+```
372+
373+This is a very simple mark file for the `action` type.
374+
375+#### `/mar/todo/update.hoon`
376+
377+```hoon
378+/- todo
379+|_ =update:todo
380+++ grab
381+ |%
382+ ++ noun update:todo
383+ --
384+++ grow
385+ |%
386+ ++ noun update
387+ --
388+++ grad %noun
389+--
390+```
391+
392+This is a very simple mark file for the `update` type.
393+
394+### Publisher
395+
396+#### `/app/todo.hoon`
397+
398+```hoon
399+/- todo
400+/+ default-agent, dbug
401+|%
402++$ versioned-state
403+ $% state-0
404+ ==
405++$ state-0 [%0 =friends:todo =tasks:todo]
406++$ card card:agent:gall
407+--
408+%- agent:dbug
409+=| state-0
410+=* state -
411+^- agent:gall
412+|_ =bowl:gall
413++* this .
414+ def ~(. (default-agent this %.n) bowl)
415+++ on-init
416+ ^- (quip card _this)
417+ `this
418+++ on-save
419+ ^- vase
420+ !>(state)
421+++ on-load
422+ |= old-state=vase
423+ ^- (quip card _this)
424+ =/ old !<(versioned-state old-state)
425+ ?- -.old
426+ %0 `this(state old)
427+ ==
428+++ on-poke
429+ |= [=mark =vase]
430+ |^ ^- (quip card _this)
431+ ?> =(src.bowl our.bowl)
432+ ?+ mark (on-poke:def mark vase)
433+ %todo-action
434+ =^ cards state
435+ (handle-poke !<(action:todo vase))
436+ [cards this]
437+ ==
438+ ++ handle-poke
439+ |= =action:todo
440+ ^- (quip card _state)
441+ ?- -.action
442+ %add
443+ :_ state(tasks (~(put by tasks) now.bowl [name.action %.n]))
444+ :~ [%give %fact ~[/updates] %todo-update !>(`update:todo`[%add now.bowl name.action])]
445+ ==
446+ ::
447+ %del
448+ :_ state(tasks (~(del by tasks) id.action))
449+ :~ [%give %fact ~[/updates] %todo-update !>(`update:todo`action)]
450+ ==
451+ ::
452+ %toggle
453+ :_ state(tasks (~(jab by tasks) id.action |=(=task:todo task(done !done.task))))
454+ :~ [%give %fact ~[/updates] %todo-update !>(`update:todo`action)]
455+ ==
456+ ::
457+ %rename
458+ :_ state(tasks (~(jab by tasks) id.action |=(=task:todo task(name name.action))))
459+ :~ [%give %fact ~[/updates] %todo-update !>(`update:todo`action)]
460+ ==
461+ ::
462+ %allow
463+ `state(friends (~(put in friends) who.action))
464+ ::
465+ %kick
466+ :_ state(friends (~(del in friends) who.action))
467+ :~ [%give %kick ~[/updates] `who.action]
468+ ==
469+ ==
470+ --
471+++ on-watch
472+ |= =path
473+ ^- (quip card _this)
474+ ?+ path (on-watch:def path)
475+ [%updates ~]
476+ ?> (~(has in friends) src.bowl)
477+ :_ this
478+ :~ [%give %fact ~ %todo-update !>(`update:todo`initial+tasks)]
479+ ==
480+ ==
481+++ on-leave on-leave:def
482+++ on-peek on-peek:def
483+++ on-agent on-agent:def
484+++ on-arvo on-arvo:def
485+++ on-fail on-fail:def
486+--
487+```
488+
489+This is the publisher agent, `todo.hoon`. The bulk of its logic is in its
490+`on-poke` arm, where it handles the various possible actions like `%add`ing a
491+task, `%toggle`ing its "done" state, `%rename`ing a task, and so on. It also has
492+a couple of `action`s for `%allow`ing and `%kick`ing subscribers.
493+
494+Most of these cases both update the state of the agent, as well as producing
495+`%fact` cards to send out to subscribers with the new data.
496+
497+You'll notice it only allows these pokes from the local ship, and enforces this
498+in `on-poke` with:
499+
500+```hoon
501+?> =(src.bowl our.bowl)
502+```
503+
504+Let's look at `on-watch`:
505+
506+```hoon
507+++ on-watch
508+ |= =path
509+ ^- (quip card _this)
510+ ?+ path (on-watch:def path)
511+ [%updates ~]
512+ ?> (~(has in friends) src.bowl)
513+ :_ this
514+ :~ [%give %fact ~ %todo-update !>(`update:todo`initial+tasks)]
515+ ==
516+ ==
517+```
518+
519+When `on-watch` gets a subscription request, it checks whether the requesting
520+ship is in the `friends` set, and crashes if it is not. If they're in `friends`,
521+it produces a `%fact` card with a null `(list path)`, which means it goes only
522+to the new subscriber. This `%fact` contains the entire `tasks` map as it
523+currently exists, getting the new subscriber up to date.
524+
525+### Subscriber
526+
527+```hoon
528+/- todo
529+/+ default-agent, dbug
530+|%
531++$ versioned-state
532+ $% state-0
533+ ==
534++$ state-0 [%0 ~]
535++$ card card:agent:gall
536+--
537+%- agent:dbug
538+=| state-0
539+=* state -
540+^- agent:gall
541+|_ =bowl:gall
542++* this .
543+ def ~(. (default-agent this %.n) bowl)
544+++ on-init
545+ ^- (quip card _this)
546+ `this
547+++ on-save
548+ ^- vase
549+ !>(state)
550+++ on-load
551+ |= old-state=vase
552+ ^- (quip card _this)
553+ =/ old !<(versioned-state old-state)
554+ ?- -.old
555+ %0 `this(state old)
556+ ==
557+++ on-poke
558+ |= [=mark =vase]
559+ ^- (quip card _this)
560+ ?> =(src.bowl our.bowl)
561+ ?+ mark (on-poke:def mark vase)
562+ %noun
563+ =/ action !<(?([%sub @p] [%unsub @p]) vase)
564+ ?- -.action
565+ %sub
566+ :_ this
567+ :~ [%pass /todos %agent [+.action %todo] %watch /updates]
568+ ==
569+ %unsub
570+ :_ this
571+ :~ [%pass /todos %agent [+.action %todo] %leave ~]
572+ ==
573+ ==
574+ ==
575+++ on-watch on-watch:def
576+++ on-leave on-leave:def
577+++ on-peek on-peek:def
578+++ on-agent
579+ |= [=wire =sign:agent:gall]
580+ ^- (quip card _this)
581+ ?+ wire (on-agent:def wire sign)
582+ [%todos ~]
583+ ?+ -.sign (on-agent:def wire sign)
584+ %watch-ack
585+ ?~ p.sign
586+ ((slog '%todo-watcher: Subscribe succeeded!' ~) `this)
587+ ((slog '%todo-watcher: Subscribe failed!' ~) `this)
588+ ::
589+ %kick
590+ %- (slog '%todo-watcher: Got kick, resubscribing...' ~)
591+ :_ this
592+ :~ [%pass /todos %agent [src.bowl %todo] %watch /updates]
593+ ==
594+ ::
595+ %fact
596+ ?+ p.cage.sign (on-agent:def wire sign)
597+ %todo-update
598+ ~& !<(update:todo q.cage.sign)
599+ `this
600+ ==
601+ ==
602+ ==
603+++ on-arvo on-arvo:def
604+++ on-fail on-fail:def
605+--
606+```
607+
608+This is the subscriber agent. Since it's just for demonstrative purposes, it has
609+no state and just prints updates it receives. In practice it would probably keep the `tasks` map it receives in its own state, and then update it as it receives new
610+`%fact`s.
611+
612+The `on-poke` arm is fairly simple - it accepts two pokes, to either `[%sub ~some-ship]` or `[%unsub ~some-ship]`.
613+
614+The `on-agent` arm will print whether a subscription request succeeded or
615+failed, as well as printing a message when it gets kicked. When it receives a
616+`%fact` from the publisher agent, it will just print it to the terminal with a
617+`~&` expression.
618+
619+### Trying it out
620+
621+We're going to try this between two different ships. The first ship will be the usual fakezod. We'll add both `mark` files, the `/sur` file, and the `todo.hoon` agent to the `%base` desk of our fakezod, putting them in the following directories:
622+
623+```
624+base
625+├── app
626+│ └── todo.hoon
627+├── mar
628+│ └── todo
629+│ ├── action.hoon
630+│ └── update.hoon
631+└── sur
632+ └── todo.hoon
633+```
634+
635+In `~zod`'s dojo, we can `|commit %base`, and then start the `%todo` agent:
636+
637+```
638+|rein %base [& %todo]
639+```
640+
641+Now we need to spin up another fake ship. We'll use `~nut` in this example:
642+
643+```
644+urbit -F nut
645+```
646+
647+Once it's booted, we can `|mount %base` and then add just the `update.hoon` mark file, the `/sur` file, and the `todo-watcher.hoon` agent like so:
648+
649+```
650+base
651+├── app
652+│ └── todo-watcher.hoon
653+├── mar
654+│ └── todo
655+│ └── update.hoon
656+└── sur
657+ └── todo.hoon
658+```
659+
660+On `~nut` we can then `|commit %base`, and start the `%todo-watcher` agent:
661+
662+```
663+|rein %base [& %todo-watcher]
664+```
665+
666+Now, on `~nut`, let's try subscribing:
667+
668+```
669+> :todo-watcher [%sub ~zod]
670+>=
671+%todo-watcher: Subscribe failed!
672+```
673+
674+Our `%todo-watcher` agent tried, but received a negative `%watch-ack` from
675+`%todo`, because we haven't yet added `~nut` to the `friends` set of allowed
676+ships. Let's now remedy that on `~zod`:
677+
678+```
679+> :todo &todo-action [%allow ~nut]
680+>=
681+```
682+
683+Let's also add a couple of to-do tasks, on `~zod`:
684+
685+```
686+> :todo &todo-action [%add 'foo']
687+>=
688+> :todo &todo-action [%add 'bar']
689+>=
690+```
691+
692+If we now check its state with `+dbug`, we'll see they're in the `tasks` map,
693+and `~nut` will also now be in the `friends` set:
694+
695+```
696+> [ %0
697+ friends={~nut}
698+ tasks
699+ { [ p=170.141.184.505.349.079.206.522.766.950.035.095.552
700+ q=[name='foo' done=%.n]
701+ ]
702+ [ p=170.141.184.505.349.079.278.538.984.166.386.565.120
703+ q=[name='bar' done=%.n]
704+ ]
705+ }
706+]
707+> :todo +dbug
708+>=
709+```
710+
711+Let's now try subscribing again on `~nut`:
712+
713+```
714+> :todo-watcher [%sub ~zod]
715+>=
716+%todo-watcher: Subscribe succeeded!
717+[ %initial
718+ tasks
719+ { [ p=170.141.184.505.349.079.206.522.766.950.035.095.552
720+ q=[name='foo' done=%.n]
721+ ]
722+ [ p=170.141.184.505.349.079.278.538.984.166.386.565.120
723+ q=[name='bar' done=%.n]
724+ ]
725+ }
726+]
727+```
728+
729+As you can see, this time it's worked, and we've immediately received the
730+initial `tasks` map.
731+
732+Now, let's try adding another task on `~zod`:
733+
734+```
735+> :todo &todo-action [%add 'baz']
736+>=
737+```
738+
739+On `~nut`, we'll see it has received the `%fact` with the new task in it:
740+
741+```
742+[ %add
743+ id=170.141.184.505.349.082.779.030.192.959.445.270.528
744+ name='baz'
745+]
746+```
747+
748+Let's try toggle its done state on `~zod`:
749+
750+```
751+> :todo &todo-action [%toggle 170.141.184.505.349.082.779.030.192.959.445.270.528]
752+>=
753+```
754+
755+`~nut` will again get the `%fact`:
756+
757+```
758+[ %toggle
759+ id=170.141.184.505.349.082.779.030.192.959.445.270.528
760+]
761+```
762+
763+Recall that incoming subscriptions are stored in `sup.bowl`, and outgoing
764+subscriptions are stored in `wex.bowl`. Let's have a look at the incoming
765+subscription on `~zod`:
766+
767+```
768+> [ path=/updates
769+ from=~nut
770+ duct=~[/gall/sys/req/~nut/todo /ames/bone/~nut/1 //ames]
771+]
772+> :todo +dbug [%incoming %ship ~nut]
773+>=
774+```
775+
776+On `~nut`, let's look at the outgoing subscription:
777+
778+```
779+> [wire=/todos agnt=[~zod %todo] path=/updates ackd=%.y]
780+> :todo-watcher +dbug [%outgoing %ship ~zod]
781+>=
782+```
783+
784+Now on `~zod`, let's try kicking `~nut` and removing it from our `friends` set:
785+
786+```
787+> :todo &todo-action [%kick ~nut]
788+>=
789+```
790+
791+On `~nut`, we'll see it got the `%kick`, tried resubscribing automatically, but
792+was rejected because `~nut` is no longer in `friends`:
793+
794+```
795+%todo-watcher: Got kick, resubscribing...
796+%todo-watcher: Subscribe failed!
797+```
798+
799+## Summary
800+
801+- Incoming subscription requests arrive in an agent's `on-watch` arm.
802+- An agent will define various subscription `path`s in its `on-watch` arm, which
803+ others can subscribe to.
804+- Gall will automatically produce a negative `%watch-ack` if `on-watch` crashed,
805+ and a positive one if it was successful.
806+- Incoming subscribers are recorded in the `sup` field of the `bowl`.
807+- `on-watch` can produce a `%fact` with a null `(list path)` which will go only
808+ to the new subscriber.
809+- Updates are sent to subscribers in `%fact` cards, and contain a `cage` with a
810+ `mark` and some data in a `vase`.
811+- `%fact`s are sent to all subscribers of the paths specified in the `(list path)`.
812+- A subscriber can be kicked from subscription paths with a `%kick` card
813+ specifying the ship in the `(unit ship)`. All subscribers of the specified
814+ paths will be kicked if the `(unit ship)` is null.
815+- An outgoing subscription can be initiated with a `%watch` card.
816+- The `%watch-ack` will come back in to the subscriber's `on-agent` arm as a
817+ `sign`, and may be positive or negative, depending on whether the `(unit tang)` is null.
818+- `%kick`s will also arrive in the subscriber's `on-agent` arm as a `sign`.
819+ Since kicks may not be intentional, the subscriber should attempt to
820+ resubscribe and only give up if the subsequent `%watch-ack` is negative.
821+- `%fact`s will also arrive in the subscriber's `on-agent` arm.
822+- All such `sign`s that arrive in `on-agent` will also have a `wire`.
823+- The `wire` for subscription updates to arrive on is specified in the initial
824+ `%watch` card.
825+- A subscriber can unsubscribe by passing a `%leave` card on the original
826+ `wire`.
827+
828+## Exercises
829+
830+- Have a look at the [Strings Guide](/docs/hoon/guides/strings) if you're
831+ already familiar with decoding/encoding atoms in strings.
832+- Try running through the [Example](#example) yourself, if you've not done so already.
833+- Try modifying `%todo-watcher` to recording the data it receives in its state,
834+ rather than simply printing it to the terminal.