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
|
# What is a "Poke"? Understanding Urbit Events
## The Big Picture
**Poke** is Urbit's term for "send an event to Arvo (the kernel)".
Think of it like this:
- **Unix**: You write to a file descriptor or call `ioctl()`
- **Windows**: You send a message to a window handle
- **Urbit**: You **poke** Arvo with an **ovum** (event)
## Event Structure: Ovum
An `ovum` (plural: ova) is a single event. It's a simple pair:
```
ovum = [wire card]
```
Where:
- **wire**: A path identifying WHERE this event came from/goes to
- Example: `/d/term/1` (Dill terminal 1)
- Example: `/g/http/0v1a2b3c` (Eyre HTTP request)
- Example: `/a/peer/~zod` (Ames network to ~zod)
- **card**: The actual action/data, structured as `[tag payload]`
- Example: `[%belt %ret]` (keyboard Return key)
- Example: `[%born ~]` (HTTP server started)
- Example: `[%send ...]` (Network packet)
## The Poke Cycle
```
┌─────────────┐
│ Runtime │ (Vere, Neovere, Sword)
└──────┬──────┘
│
│ 1. Poke with ovum [wire card]
│
▼
┌─────────────┐
│ Arvo │ (The kernel)
└──────┬──────┘
│
│ 2. Returns [effects new_kernel]
│
▼
┌─────────────┐
│ Runtime │
└──────┬──────┘
│
│ 3. Execute effects
│ (print to terminal, send network packet, etc.)
│
└─────► Real world I/O
```
## How Poke Actually Works
From the Vere C code (and our OCaml port):
```ocaml
(* Get poke function from kernel *)
let poke_arm = slot 23 kernel in
(* Create gate (function) *)
let gate = nock kernel poke_arm in
(* Call gate with event *)
let result = slam gate event in
(* Result is [effects new_kernel] *)
match result with
| Cell (effects, new_kernel) ->
(* Update kernel state *)
kernel := new_kernel;
(* Execute effects *)
execute_effects effects
```
## Types of Events (Common Cards)
### Terminal (Dill)
```
Wire: /d/term/1
Inputs (to Arvo):
[%belt %ret] - Return key
[%belt %bac] - Backspace
[%belt [%txt "hello"]] - Text input
Outputs (from Arvo):
[%blit %lin "text"] - Print line
[%blit %clr] - Clear screen
[%blit %hop 5] - Move cursor
```
### HTTP (Eyre)
```
Wire: /g/http/0v...
Inputs:
[%request request-data] - HTTP request
Outputs:
[%http-response response] - HTTP response
```
### Network (Ames)
```
Wire: /a/peer/~zod
Inputs:
[%hear packet] - Received packet
Outputs:
[%send packet] - Send packet
```
### Timer (Behn)
```
Wire: /b/wait/...
Inputs:
[%wake ~] - Timer fired
Outputs:
[%doze time] - Set next timer
```
## Boot vs Runtime
### During Boot (Lifecycle)
```ocaml
(* Boot uses a special formula, not poke! *)
let lifecycle = [2 [0 3] [0 2]] in
let kernel = nock event_list lifecycle in
(* No effects during boot - just builds kernel *)
```
Boot events are **batch processed** via the lifecycle formula.
They build up the kernel state but don't produce effects.
### After Boot (Runtime)
```ocaml
(* Runtime uses poke for each event *)
let effects = poke kernel event in
(* Effects need to be executed! *)
```
Runtime events are **individually poked** and produce effects that must be executed.
## Why Two Different Mechanisms?
**Lifecycle** (boot):
- Process many events as a batch
- Pure state building
- No I/O needed
- Fast initialization
**Poke** (runtime):
- Process one event at a time
- Produces side effects
- Needs I/O execution
- Interactive operation
## Our Implementation
### State.poke (lib/state.ml:66)
```ocaml
let poke state event =
(* Get poke gate from kernel slot 23 *)
let formula = slot poke_formula_axis kernel in
let gate = nock_on kernel formula in
(* Slam gate with event *)
let result = slam_on gate event in
(* Extract effects and new kernel *)
match result with
| Cell (effects, new_core) ->
state.roc <- new_core; (* Update kernel *)
state.eve <- succ eve; (* Increment event number *)
effects (* Return effects *)
```
### What We Need To Do Next
1. ✅ **Poke works** - ~11,000 pokes/second
2. ✅ **Effects returned** - Structure is correct
3. ⏳ **Parse effects** - Extract wire/card from effect list
4. ⏳ **Route effects** - Send to appropriate driver (Dill, Eyre, Ames)
5. ⏳ **Execute effects** - Actually do the I/O
6. ⏳ **Event loop** - Continuous input → poke → effects → output
## Example: Terminal Input Flow
```
1. User types "hello" and hits Enter
2. Terminal driver creates belt event:
ovum = [/d/term/1 [%belt [%txt "hello"]]]
[/d/term/1 [%belt %ret]]
3. Poke Arvo:
effects = State.poke state ovum
4. Arvo processes input, maybe runs dojo command
5. Arvo returns effects:
[
[/d/term/1 [%blit [%lin "result here"]]]
[/d/term/1 [%blit [%lin "~zod:dojo> "]]]
]
6. Dill driver executes effects:
- Print "result here" to terminal
- Print prompt "~zod:dojo> "
7. Wait for next input
```
## Performance Notes
From our benchmarks:
- **Poke speed**: ~11,000 pokes/second (0.09ms each)
- **Boot time**: ~1s for full solid pill
- **Memory**: No limit (OCaml GC manages it)
This is plenty fast for interactive use!
## Key Insight
**Poke is just a function call into the kernel.**
There's no magic. It's:
1. Load kernel from memory
2. Call its poke function (slot 23)
3. Get back effects + new kernel
4. Replace old kernel with new kernel
5. Execute the effects
The genius is that the **entire OS state** is in that kernel noun, and poke is a pure function that transforms it.
## References
- `vere/pkg/vere/mars.c` - C implementation
- `vere/pkg/noun/vortex.c:u3v_poke()` - Poke implementation
- `docs/runtime/api.md` - API documentation (check `/docs/runtime/`)
- Arvo source: `sys/arvo.hoon` in urbit/urbit repo
## See Also
- `BOOT_COMPARISON.md` - Why lifecycle ≠ poke
- `EFFECTS.md` - Effects processing architecture
- `LOOM.md` - Memory management (or lack thereof!)
|