1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
|
---
description: "Core Academy lesson on Arvo vanes covering the Behn timer vane, Dill terminal driver vane, Khan thread runner vane, Lick IPC vane."
layout:
title:
visible: true
description:
visible: false
tableOfContents:
visible: true
outline:
visible: true
pagination:
visible: true
---
# 8. Vanes I: Behn, Dill, Kahn, Lick
*In this lesson we'll look at Arvo's timer vane, Behn, and Dill, the terminal driver vane. This includes discussion of the Dojo terminal, and the userspace/kernelspace interfaces `%helm` and `%hood`.*
A **vane** is an Arvo kernel module that performs essential system operations. The vanes are:
- Ames, the peer-to-peer networking vane.
- Behn, the timer vane.
- Clay, the filesystem, revision-control and build system vane.
- Dill, the terminal-driver vane.
- Eyre, the HTTP vane.
- Gall, the application vane.
- Iris, the server HTTP vane.
- Jael, the security vane.
- Khan, the control vane.
- Lick, the interprocess communication (IPC) vane.
> As described above, we use Arvo proper to route and control the flow of `move`s. However, Arvo proper is rarely directly responsible for processing the event data that directly causes the desired outcome of a `move`. This event data is contained within a `card`. Instead, Arvo proper passes the `card` off to one of its vanes, which each present an interface to clients for a particular well-defined, stable, and general-purpose piece of functionality.
## Vane Interface {#vane-interface}
Arvo is a message dispatcher, which doesn't really know about the vanes except via their existence in a `van=(map term vane)` in Arvo's `$soul`.
Formally, a vane must be a “vane-shaped noun”—an interface presenting the arms:
```hoon
|%
:: +call: handle a +task request
++ call
:: +load: migrate an old state to a new vane version
++ load
:: +scry: view vane state at a particular /path
++ scry
:: +stay: extract state before reload
++ stay
:: +take: handle $response sign
++ take
--
```
- `+load` and `+stay` are necessary to update the vane.
- `+call` is used to pass a request in (“advance to target”).
- `+scry` exposes the read-only scry namespace of the vane.
- `+take` receives a response from another vane (“retreat along call stack”).
(Now the formerly-elided distinctions between `sign`, `gift`, `task`, and `note` start to matter.)

- A `note` is sent by a vane to the Arvo kernel's `+call` arm.
- Arvo dispatches a `task` to a vane's `+call` arm.
- The vane performs the work.
- If a result needs to be passed back, it is emitted as a `gift` along the `duct` back to Arvo's `+take` arm.
- Arvo dispatches a `sign` to the original caller's `+take` arm.
The actual mechanics of this are that the moves are placed into the appropriate `duct`, which is a `(list wire)`, simply an ordered collection of moves representing the causal history.
`/sys/arvo` tracks what little it knows about vanes at a few points, e.g.:
```hoon
:: van: vanes while we desire it (in larval stage)
van=(map term (trap vase))
::
++ grow
|= way=term
?+ way way
%a %ames
%b %behn
%c %clay
%d %dill
%e %eyre
%g %gall
%i %iris
%j %jael
%k %khan
%l %lick
==
::
+$ vane [=vase =worm]
```
- `vase` is of course a generic vase, but specifically it expects a noun with the correct `$type`.
- `worm` is the worm cache as discussed in [*Arvo II: The Boot Sequence*](ca04.md).
Arvo interacts with vanes in vase mode; for instance, a scry takes place via a call to the `+scry` arm via a `+slap` against the `%limb` named `%scry`: `(~(slap wa sac) rig [%limb %scry])`. As usual, working in vase mode permits dynamic updates to the source.
Vanes have as their subject:
- `/sys/hoon` for language definitions.
- `/sys/arvo` for message dispatch.
- `/sys/lull` for a shared interface definition.
- `/sys/zuse` for various stdlib utilities.
In particular, `/sys/lull` acts as a header so that vanes can “see” each other's interface.
### Updates {#updates}
As with other parts of the system, vanes are rebuilt if the inner core on which they rely has been updated or if the vane itself has changed.
An update to a vane is triggered by `+mod:what:pith` in the `+le` event-loop engine. (Recall from `ca05` that `+what` is involved in a system upgrade.) While there are some unfamiliar types here, note particularly the `%=` centis clause building each vane.
```hoon
++ mod
|= [del=news all=?]
^+ ..pith
=^ job=oped fat.mod.sol (~(adorn adapt fat.mod.sol) del all)
=? lul.mod.sol ?=(^ lul.job)
(smit:va "lull" pit /sys/lull/hoon u.lul.job)
=? zus.mod.sol ?=(^ zus.job)
(smit:va "zuse" lul.mod.sol /sys/zuse/hoon u.zus.job)
%- %+ need:wyrd kel.ver.zen
:~ lull/;;(@ud q:(slap lul.mod.sol limb/%lull))
zuse/;;(@ud q:(slap zus.mod.sol limb/%zuse))
==
%= ..pith
van.mod
%+ roll van.job
|= [[nam=term txt=cord] van=_van.mod.sol]
^+ van
=/ nex (create:va our zus.mod.sol nam /sys/vane/[nam]/hoon txt)
=/ nav (~(get by van) nam)
=? nex ?=(^ nav) (update:va vase.u.nav nex)
(~(put by van) nam (settle:va nex))
==
```
The recompilation against `%zuse` takes place in `+adorn:adapt:part`. ([Arvo Pärt](https://www.youtube.com/watch?v=jNxbT0MESTY))
```hoon
:: kernel modules
::
:: %zuse is the subject of the vanes; force all if we have a new %zuse
::
=. all |(all ?=(^ zus))
=| nav=(map term cord)
=? nav all
%- ~(gas by nav)
%+ turn
~(tap by dir:(~(dip of fat) /sys/vane))
|=([name=@ta _fat] [`@tas`name (sole (need fil))])
```
## Behn {#behn}
Behn is a timer/wake-up call system. Since it's a simple vane, let's approach it obliquely, by looking at a generator that calls it.
- Open `/base/gen/timers/hoon` and examine the code.
```hoon
.^((list [date=@da =duct]) %bx (en-beam [our %$ [%da now]] /debug/timers))
```
- [“Behn Overview”](/reference/arvo/dill/behn)
### `/sys/lull` Definition {#syslull-definition}
The interface to Behn is defined in `/sys/lull`:
```hoon
:: ::::
:::: ++behn :: (1b) timekeeping
:: ::::
++ behn ^?
|%
+$ gift :: out result <-$
$% [%doze p=(unit @da)] :: next alarm
[%wake error=(unit tang)] :: wakeup or failed
[%meta p=vase]
[%heck syn=sign-arvo] :: response to %huck
==
+$ task :: in request ->$
$~ [%vega ~] ::
$% $>(%born vane-task) :: new unix process
[%rest p=@da] :: cancel alarm
[%drip p=vase] :: give in next event
[%huck syn=sign-arvo] :: give back
$>(%trim vane-task) :: trim state
$>(%vega vane-task) :: report upgrade
[%wait p=@da] :: set alarm
[%wake ~] :: timer activate
==
-- ::behn
```
(A `%vega` task informs the vane that the kernel has been upgraded.)
### Structure {#structure}
```hoon
+$ behn-state
$: %2
timers=(tree [key=@da val=(qeu duct)])
unix-duct=duct
next-wake=(unit @da)
drips=drip-manager
==
```
- How does Behn think of timers?
`/sys/behn` presents three primary cores:
1. A type definition core.
2. A helper core.
3. The primary vane interface.
Behn only has two kinds of moves: `%wait` `task`s and `%wake` `gift`s. How are these processed and what do they result in? (See the `$timer-map` structure too.)
- What is a drip? How is it used?
> Say an app (the Target) is subscribed to updates from Clay (the Client). If Clay `%give`s updates to the app directly and the app crashes, this may cause Clay to crash as well. If instead Clay `%pass`es Behn a `%drip` `task` wrapping the update `gift`, Behn will set a timer for `now` that, when fired, will cause the update `gift` to be given. If it causes a crash then it will have been in response to the `%drip` move, thereby isolating Clay from the crash. Thus `%drip` acts as a sort of buffer against cascading sequences of crashes.
### The Nested Core Pattern {#the-nested-core-pattern}
(~rabsef-bicrym calls this the “`+abet` engine” and while it's not a popular term inside of the core development team, I like the pithiness of it. “Engine” as a term of art is frequently used in the kernel to refer to `+le` two-letter doors so this usage is not completely inconsistent, just more specialized in application.)
The basic concept of the nested core pattern is to have an outer core which builds a list of cards and state changes, then produces the queued changes all at once. (I always think about this as being one of those wind-up cars that you crank and then set down to whir away.)
Behn's nested core pattern is pretty simple: it has an alias to `this`, one `+emit` arm to prepend a move to a list of moves, and an `+abet` arm to yield the `[moves state]`. The `+per-event` core is used to script the neighboring `+scry` and `+call` arms for the vane without leaking state invariants. Behn's instantiation of the `+abet` pattern centralizes the helper outer core as a centralized state machine.
These are some common `+abet` pattern arms. These are not all unique, and many cores will omit all or most of these.
- `+abed`—initialize. Set up the state of the inner core.
- `+yoke`—initialize. Start from a particular value.
- `+abet`—finalize. Exit from an inner core to an outer core, taking changes. Commonly, take a modified valued and overwrite it into the final state with a `+put:by`.
- `+abut`—finalize. Alternative exit from `+abet` for a deletion.
- `+move`—send a move. Generalization for `+pass`/`+give`.
- `+pass`—request an action. Prepend a `%pass` move to the current list of moves.
- `+give`—return a result. Prepend a standard `%give` to the current list of moves.
- `+emit`—submit a card. Prepend a card to the current list of cards.
- `+emil`—submit cards. Prepend a list of cards to the current list of cards.
If some state needs to be maintained, this can be built in a door, but Behn's particular example is even more basic. In files with associated doors or with multiple nested core instances, it is common to prepend a two-letter identifier to disambiguate which outer core is being scripted at any given time, such as `+mo-abet` or `+ap-emil`.
- [The Engine Pattern](../../hoon/engine-pattern.md)
### Vere I/O Driver: `vere/io/behn.c` {#vere-io-driver-vereiobehnc}
Arvo acquires its timer updates from Unix via `vere/io/behn.c`. This file presents its primary interface at `u3_behn_io_init()` to initialize a timer. This simply retrieves the current Unix time for a starting point and sets up the interface.
Each communicating vane is linked various I/O drivers in `vere/auto.c`. (These do not correspond one-to-one with vanes.) These are registered into `car_u`, a global `_u3_auto` used for I/O driver invocations and callbacks.
For Behn, the timer is set in `_behn_ef_doze()` using `uv_timer_start()`. The corresponding wakeup timer is emitted in `_behn_time_cb()` when the `libuv` main event loop handler.
Finally, we can examine how the injection comes back into Arvo in `_behn_time_cb()`. An `ovum` is produced by `u3_ovum_init()`, manually injected using `u3_auto_plan()`, and subscribed to with `u3_auto_peer()`. `/sys/behn` then processes this wakeup event as a `%wake` via its `+call` arm.
## Dill {#dill}
Dill is Urbit's terminal driver.
```hoon
|pass [%d %text "hello world"]
```
Dill as a vane is mostly responsible for actually constructing terminal sessions and coordinating input and output. Thus most of the terminal stack actually lives in userspace (instrumented by Gall) rather than in `/sys/dill`.
What do we mean when we talk about a terminal? Originally, of course, computer were directly programmed by moving wires between vacuum tubes or chips; later, this evolved to the ability to read and output cards. Computer terminals with CRT-based character displays began to be used in the late 1950s and gradually became more common. In fact, the original plasma display screens were used with PLATO in the 1970s–1990s.

When we refer to the terminal today, we typically mean a modern [terminal emulator](https://en.wikipedia.org/wiki/Terminal_emulator), which presents a terminal-like text user interface (TUI) for software to treat as if it were an actual character display. Terminal emulators need to track information like dimensions ($x$, $y$), content layout, active sessions or connexions, and cursor position. They provide affordances like a color space, escape codes, layout libraries, and scrollable sessions.
Dill is responsible for interfacing with keystrokes and with the terminal emulator session. Since Urbit can be run in a daemon mode, it's not necessary for Dill to actually have a terminal session for Urbit to run.
Due to terminal emulator limitations, Dill sessions are only properly supported today in the `%webterm` app.
- [“Developer Call: Urbit’s improved Terminal Stack”](https://www.youtube.com/watch?v=E-6E-l1SxFw)
- [“Dill Overview”](/reference/arvo/dill/dill)
### `/sys/lull` Definition {#syslull-definition}
The `/sys/lull` interface specification for Dill is more complicated than that of Behn. Unlike Behn, a number of supporting types are necessary to produce the basic pair of `gift`/`task` for Dill.
```hoon
:: ::::
:::: ++dill :: (1d) console
:: ::::
++ dill ^?
|%
+$ gift :: out result <-$
$% [%blit p=(list blit)] :: terminal output
[%logo ~] :: logout
[%meld ~] :: unify memory
[%pack ~] :: compact memory
[%trim p=@ud] :: trim kernel state
[%logs =told] :: system output
== ::
+$ task :: in request ->$
$~ [%vega ~] ::
$% [%boot lit=? p=*] :: weird %dill boot
[%crop p=@ud] :: trim kernel state
[%flog p=flog] :: wrapped error
[%heft ~] :: memory report
$>(%init vane-task) :: after gall ready
[%logs p=(unit ~)] :: watch system output
[%meld ~] :: unify memory
[%pack ~] :: compact memory
[%seat =desk] :: install desk
[%shot ses=@tas task=session-task] :: task for session
$>(%trim vane-task) :: trim state
$>(%vega vane-task) :: report upgrade
[%verb ~] :: verbose mode
[%knob tag=term level=?(%hush %soft %loud)] :: deprecated removeme
session-task :: for default session
told :: system output
== ::
:: ::
+$ session-task :: session request
$% [%belt p=belt] :: terminal input
[%blew p=blew] :: terminal config
[%flee ~] :: unwatch session
[%hail ~] :: terminal refresh
[%open p=dude:gall q=(list gill:gall)] :: setup session
[%shut ~] :: close session
[%view ~] :: watch session blits
== ::
:: ::
+$ told :: system output
$% [%crud p=@tas q=tang] :: error
[%talk p=(list tank)] :: tanks (in order)
[%text p=tape] :: tape
== ::
::
:::: :: (1d2)
::
+$ blew [p=@ud q=@ud] :: columns rows
+$ belt :: client input
$? bolt :: simple input
[%mod mod=?(%ctl %met %hyp) key=bolt] :: w/ modifier
[%txt p=(list @c)] :: utf32 text
== ::
+$ bolt :: simple input
$@ @c :: simple keystroke
$% [%aro p=?(%d %l %r %u)] :: arrow key
[%bac ~] :: true backspace
[%del ~] :: true delete
[%hit x=@ud y=@ud] :: mouse click
[%ret ~] :: return
== ::
+$ blit :: client output
$% [%bel ~] :: make a noise
[%clr ~] :: clear the screen
[%hop p=$@(@ud [x=@ud y=@ud])] :: set cursor col/pos
[%klr p=stub] :: put styled
[%mor p=(list blit)] :: multiple blits
[%nel ~] :: newline
[%put p=(list @c)] :: put text at cursor
[%sag p=path q=*] :: save to jamfile
[%sav p=path q=@] :: save to file
[%url p=@t] :: activate url
[%wyp ~] :: wipe cursor line
== ::
+$ dill-belt :: arvo input
$% belt :: client input
[%cru p=@tas q=(list tank)] :: errmsg (deprecated)
[%hey ~] :: refresh
[%rez p=@ud q=@ud] :: resize, cols, rows
[%yow p=gill:gall] :: connect to app
== ::
+$ dill-blit :: arvo output
$% blit :: client output
[%qit ~] :: close console
== ::
+$ flog :: sent to %dill
$% [%crop p=@ud] :: trim kernel state
$>(%crud told) ::
[%heft ~] ::
[%meld ~] :: unify memory
[%pack ~] :: compact memory
$>(%text told) ::
[%verb ~] :: verbose mode
== ::
:: ::
+$ poke :: dill to userspace
$: ses=@tas :: target session
dill-belt :: input
== ::
-- ::dill
```
The main concepts to keep in mind:
- Dill receives `%belt` `task`s and sends `%blit` `gift`s.
- `%belt` `task`s result from keystrokes, terminal resizing,
- `%blit` `gift`s result from output events: putting a character, clearing the screen, placing the cursor.
### Structure {#structure}
Dill's primary state is its `$axle` with a logging level:
```hoon
|% :: console protocol
+$ axle ::
$: %7 ::
hey=(unit duct) :: default duct
dug=(map @tas axon) :: conversations
eye=(jug @tas duct) :: outside observers
ear=(set duct) :: syslog listeners
lit=? :: boot in lite mode
egg=_| :: see +take, removeme
== ::
+$ axon :: dill session
$: ram=term :: console program
tem=(unit (list dill-belt)) :: pending, reverse
wid=_80 :: terminal width
== ::
+$ log-level ?(%hush %soft %loud) :: none, line, full
--
```
As with `/sys/behn`, the primary cores of Dill include:
1. Type definitions (two cores).
2. Helper core (`+as` per-cause engine).
3. The primary vane interface.
A good way to familiarize yourself with Dill operations is to follow the full lifecycle of input and output in the next section.
Dill is the first vane in the boot sequence, and is used to boot Jael. (Compare `%aqua`, which does not need to start Dill and can initialize Jael directly.)
### Vere I/O Driver: `vere/io/term.c`, `ptty.c` {#vere-io-driver-vereiotermc-pttyc}
The main entrypoint for the terminal is `u3_term_io_init()`, which simply sets up the interface and callbacks.
As we noted before, each communicating vane is linked various I/O drivers in `vere/auto.c`. Here the global `car_u` has `term.c` connected for invocations and callbacks.
**`vere/vere.h`**:
```c
// u3_term_start_spinner(): prepare spinner state. RETAIN.
void u3_term_start_spinner(u3_noun say, c3_o del_o);
// u3_term_stop_spinner(): reset spinner state and restore input line.
void u3_term_stop_spinner(void);
// u3_term_get_blew(): return window size [columns rows].
u3_noun u3_term_get_blew(c3_l tid_l);
// u3_term_ef_winc(): window change.
void u3_term_ef_winc(void);
// u3_term_ef_ctlc(): send ^C.
void u3_term_ef_ctlc(void);
// u3_term_io_init(): initialize terminal I/O.
u3_auto* u3_term_io_init(u3_pier* pir_u);
// u3_term_io_hija(): hijack console for cooked print.
FILE* u3_term_io_hija(void);
// u3_term_it_log(): writes a log message
void u3_term_io_log(c3_c* line);
// u3_term_io_loja(): release console from cooked print.
void u3_term_io_loja(int x, FILE* f);
// u3_term_log_init(): initialize terminal for logging
void u3_term_log_init(void);
// u3_term_log_exit(): clean up terminal.
void u3_term_log_exit(void);
// u3_ptty_init(): initialize platform-specific tty.
u3_utty* u3_ptty_init(uv_loop_t* lup_u, const c3_c** err_c);
```
For instance, a keystroke is processed in the following way:
- `uv_read_start()` is the `libuv` event loop injector.
- `_term_read_cb()` is the character keystroke callback.
- `_term_suck()` processes input.
- `_term_io_suck_char()` decides if it's an `xterm` terminal emulator issue or something for Arvo to know about.
- `_term_io_spit()` inputs the buffer and belt.
- `_term_io_belt()` actually sends a value along the belt (as an `ovum`).
Now in Arvo, the keystroke is routed to Dill:
- `+call` takes the `task` from Arvo.
- `+call:as` receives the input and dispatches on `%belt`.
- `+send:as` sends the action to the proper session.
- `+deal:as` signals to pass the keystroke to Gall.
- `+pass:as` executes the pass to Gall.
Output can happen in three ways:
1. Some output traverses a path from (say) Gall outbound. These are conventionally known as `%slog`s.
- Somehow a noun is marked for output using a `%slog` hint:
- `~&` sigpam does this directly.
- `~>` siggar can do this with a `%slog` hint and a priority value.
- `+slog` wraps this as a function.
Back in the runtime:
- Once we have a `%slog` hint for the runtime, it can be emitted from the Nock processor via Nock Eleven. `noun/nock.c:_n_bint()` dispatches this via `SLOG` and thence `do_slog` in the bytecode processor.
- `noun/trace.c:u3t_slog()` prints a value directly through the `u3C.slog_f` print handler, which is `_cw_serf_send_slog()`.
- `vere/main.c::_cw_serf_send_slog()` sends the hint output to the serf.
- `vere/main.c:_cw_serf_send()` is a plea handler to send pleas to the daemon.
- `vere/newt.c:u3_newt_send()` sends a `+jam`med noun of the output (`u3s_jam_xeno()`) as a buffer to a stream. In this case, the value ends up at the `libuv` buffer using `uv_write()`.
In this case, output results in the fact of the layout of the terminal handler rather than being explicitly known about by Dill. As a consequence, if you are building a TUI application and you don't want to have misaligned output, you need to build it directly using `%blit`s and suppress `%slog`s within your app. (See `tui-toys` for an example in `%snek`.)
2. Other output goes via Dill because the terminal vane explicitly needs to know about its position. This are `%blit`s.
- A program (like `%snek`) can specify to manually output a `%blit` like `%klr` (styled text) or `%put` (plain text) at the cursor.
- [`/lib/etui`](https://github.com/urbit/urbit/blob/wip/tui-toys/pkg/demo/lib/etui.hoon) offers some interesting demonstrations in this vein; see the `+zo` engine.
- `vere/io/term.c:_term_io_kick()` applies effects sent to `term.c`, including blits. It was registerd as the effect handler when the I/O drivers were registered.
- `vere/io/term.c:_term_ef_blit()` switches on the type of blit (notice it can also track a cursor position).
- `vere/io/term.c:_term_it_show_tour()` emits UTF-32 to the cursor location.
- Finally, `vere/io/term.c:_term_it_show_line()` prints at the actual cursor position.
3. The runtime terminal manager automatically handles aspects of layout such as maintaining the input line at the bottom of the screen. See `vere/io/term.c:_term_it_restore_line()` for details. (CSI = Control Sequence Introducer sequence) A different terminal handler (like `%webterm`) may handle these decisions differently.
Other output may follow yet a different path; for instance, `u3l_log()` directly prints using `vsnprintf()`to `stderr`. Some output goes via the `libuv` event loop, such as `u3_ptty_init()`..
- Examine `u3_term_log_init()` in `vere/io/term.c`.
- Examine `_term_it_show_line()` in the same file.
### Dojo, `%hood`, `%helm`, `%drum` {#dojo-hood-helm-drum}
Dojo is Urbit's primary CLI interface, and while it is too complicated to delve deeply into here, the major parts to consider include:
1. Hoon parser. Real-time parsing of input, which evaluates Hoon code for syntactical correctness. (This is the reason that typing at the Dojo prompt is frequently slower than typing at other CLIs.)
2. Specialized syntax. Besides Hoon input, generators and pokes can be set up and invoked directly from the Dojo prompt. `%say` and `%ask` generators are head-tagged pairs with gates following that return `sole-result`s.
1. `+generator` prefixes cause Dojo to look in `/gen` for a particular generator file.
2. `|generator` is a Hood generator, on which more in a moment.
3. `+desk!generator` invokes a generator on a particular desk.
4. `:agent|generator` takes the output from a generator (at `/gen/agent/generator`) and feeds it as a noun to the agent's `+on-poke` arm.
5. `-thread` invokes a thread, with a similar desk-specific prefix as above.
3. Hood. Most of the interesting Urbit instrumentation is provided to Dojo by the Hood/Helm agent pipeline.
1. `%hood` is the overarching system app, to which Dojo redirects generator invocations prefixed with `|` bar such as `|pass` and `|install`. (Thus, `|pass` is in fact shorthand for `:hood|pass`.)
2. `%helm` provides the interface for kernel and system functionality, such as `|verb`, `|moon`, etc. Hood calls into Helm.
3. `%drum` manages the active CLI apps (`|dojo/link`, `Ctrl+X`).
4. `%kiln` instruments filesystem operations using Clay.
The overall call web of these is surprisingly tangled; as an example, let's trace `|dojo/link`, which tells Dojo to register a CLI interface to a Gall agent.
- `/gen/hood/link`
- `/app/hood`
- `/lib/drum`→`+poke` w/ `%drum-link`
- `/lib/drum`→`+poke-link`
- `/lib/drum`→`+se-link`
- `eel` (what is this?)
## Exercises {#exercises}
- Trace the entire lifecycle of `|pass [%d %text "Hello Mars!"]`. Include a function-by-function annotation and commentary.
- Write a basic vane, `/sys/vane/`. `~&` on receiving a `task`.
|