├── .gitignore ├── development ├── runtime │ ├── resources │ │ └── jtrace.png │ └── README.md └── hoon │ └── README.md ├── README.md ├── operations ├── hosting │ └── README.md └── dojo │ └── README.md └── urbit └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /development/runtime/resources/jtrace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashelkovnykov/urbit-faq/HEAD/development/runtime/resources/jtrace.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Urbit FAQ 2 | 3 | This repo contains non-trivial info that I have asked or have seen others ask, as well as the answers we received. At 4 | one time it contained more information, but much of it has been documented in 5 | [the main Urbit docs](https://docs.urbit.org). The ultimate goal is to have all of this information in the main 6 | docs/guides, one day. Until then, the goals of this repo are: 7 | 8 | 1. Help new Hoon devs and Urbit pilots 9 | 2. Prevent Hoon/Urbit long-beards from answering the same questions over and over 10 | 3. Catalogue rare information so that it doesn't fall through the cracks 11 | 12 | Where possible, I've included the source of the information, additional sources that provide context, and a place in the 13 | docs where I think this information should be documented. 14 | -------------------------------------------------------------------------------- /operations/hosting/README.md: -------------------------------------------------------------------------------- 1 | # Hosting 2 | 3 | This section contains information about hosting ships. 4 | 5 | ## Contents 6 | 7 | [Access web interface over SSH](#access-web-interface-over-ssh) \ 8 | [Event log: `chop` vs. `roll`](#event-log-chop-vs-roll) \ 9 | [Hosting on a Raspberry Pi](#hosting-on-a-raspberry-pi) \ 10 | [Hosting on a VPS](#hosting-on-a-vps) \ 11 | [Monitoring uptime of ships](#monitoring-uptime-of-ships) 12 | 13 | ### Access web interface over SSH 14 | 15 | If you create a host definition for your planet in your `~/.ssh/config` like so: 16 | ``` 17 | Host urbit 18 | Hostname 19 | User urbit 20 | Port 22 21 | IdentityFile ~/.ssh/ 22 | LocalForward 9000 127.0.0.1:8080 23 | ServerAliveInterval 60 24 | ``` 25 | then if you establish an SSH connection to the server hosting your ship, you can connect to `localhost:9000` in your 26 | local browser to access the Landscape web interface of your ship over the SSH tunnel, completely bypassing HTTP. 27 | 28 | ***source:*** *`~matwel-mitreb`*\ 29 | ***context:*** *NONE* \ 30 | ***location:*** *TODO* 31 | 32 | ### Event log: `chop` vs. `roll` 33 | 34 | `roll` creates a new event log "epoch", whereas `chop` creates a new event log epoch and then deletes all but the two 35 | most recent epochs. 36 | 37 | ***source:*** *`~mastyr-bottec`*\ 38 | ***context:*** https://github.com/urbit/vere/releases/tag/vere-v3.0 \ 39 | ***location:*** https://docs.urbit.org/manual/running/vere#utilities 40 | 41 | ### Hosting on a Raspberry Pi 42 | 43 | A Raspberry Pi would work for hosting a star, depending on what else is running on the Pi, competing with Urbit for 44 | RAM and SD reader access. Most likely, you would want a Pi with 4GB+ RAM and an SSD attached by USB. 45 | 46 | ***source:*** *`~monted-tallex`*\ 47 | ***context:*** https://botter-nidnul.github.io/Steps_to_Urbit_on_Raspberry_Pi.html \ 48 | ***location:*** *TODO* 49 | 50 | ### Hosting on a VPS 51 | 52 | A GitHub repo containing easily-configurable files for automatically setting up a VPS environment to host Urbit ships, 53 | as well as (optionally) websites and MinIO is available 54 | [here](https://github.com/ashelkovnykov/urbit-hosting). 55 | 56 | ***source:*** *`~datder-sonnet`, `~finmep-lanteb`*\ 57 | ***context:*** *NONE*\ 58 | ***location:*** *TODO* 59 | 60 | ### Monitoring uptime of ships 61 | 62 | Using the script available [here](https://github.com/mrdomino/urbit-sysops/blob/master/sbin/urbit-exporter), you can 63 | set up [Prometheus](https://prometheus.io/) to monitor any number of running Urbit instances. Prometheus is a re-write 64 | of Borgmon, a Google-internal, time-series monitoring system. 65 | 66 | ***source:*** *`~silsyn-wathep`*\ 67 | ***context:*** *NONE*\ 68 | ***location:*** *TODO* 69 | -------------------------------------------------------------------------------- /operations/dojo/README.md: -------------------------------------------------------------------------------- 1 | # Dojo 2 | 3 | This section contains information about using the Dojo (the Urbit ship terminal). 4 | 5 | ## Contents 6 | 7 | [Command: call thread from custom desk](#command-call-thread-from-custom-desk) \ 8 | [Command: check if ship is live or fake](#command-check-if-ship-is-live-or-fake) \ 9 | [Command: crash the pretty printer](#command-crash-the-pretty-printer) \ 10 | [Command: `curl` from dojo](#command-curl-from-dojo) \ 11 | [Command: get docket for application](#command-get-docket-for-application) \ 12 | [Command: get latest hash of desk](#command-get-latest-hash-of-desk) \ 13 | [Command: poke an agent on another ship from dojo](#command-poke-an-agent-on-another-ship-from-dojo) \ 14 | [Command: print jammed noun to dojo](#command-print-jammed-noun-to-dojo) \ 15 | [Command: replace a broken vane on a ship](#command-replace-a-broken-vane-on-a-ship) \ 16 | [Command: what do the different command symbols mean?](#command-what-do-the-different-command-symbols-mean) \ 17 | [Error: dojo stuck on `%dy-no-prompt`](#error-dojo-stuck-on-dy-no-prompt) \ 18 | [Error: "leak" messages in dojo after Vere crash](#error-leak-messages-in-dojo-after-vere-crash) \ 19 | [What does `|meld` do?](#what-does-meld-do) \ 20 | [What does `|pack` do?](#what-does-pack-do) \ 21 | [What does the output of `|mass` mean?](#what-does-the-output-of-mass-mean) \ 22 | [What is the "base" hash in `+vats`?](#what-is-the-base-hash-in-vats) \ 23 | [What is the "%cz" hash in `+vats`?](#what-is-the-cz-hash-in-vats) 24 | 25 | ### Command: call thread from custom desk 26 | 27 | To call a thread `-foo` from the desk `%bar`, use the following command in the dojo: 28 | ``` 29 | > -bar!foo 30 | ``` 31 | 32 | ***source:*** *`~tinnus-napbus`* \ 33 | ***context:*** *TODO* \ 34 | ***location:*** *TODO* 35 | 36 | ### Command: check if ship is live or fake 37 | 38 | The following scry will return true (a.k.a. `%.y` a.k.a. `0`) if a ship is fake (i.e. using fake network keys): 39 | ``` 40 | .^(? %j /=fake=) 41 | ``` 42 | 43 | ***source:*** *`~tinnus-napbus`* \ 44 | ***context:*** *TODO* \ 45 | ***location:*** *TODO* 46 | 47 | ### Command: crash the pretty printer 48 | 49 | - `!>(sign-arvo)` 50 | - `|.(1)` 51 | - Generally, printing any type or trap will work 52 | 53 | ***source:*** *`~sidnym-ladrut`, `~wispem-wantex`* \ 54 | ***context:*** *TODO* \ 55 | ***location:*** *TODO* 56 | 57 | ### Command: `curl` from dojo 58 | 59 | You can get the response of an HTTP `curl` to a URL from dojo using the following syntax: 60 | 61 | ``` 62 | > +https://example.com 63 | 64 | HTTP 200 65 | age: 225736 66 | cache-control: max-age=604800 67 | content-type: text/html; charset=UTF-8 68 | date: Thu, 11 Apr 2024 00:43:37 GMT 69 | etag: "3147526947+gzip+ident" 70 | expires: Thu, 18 Apr 2024 00:43:37 GMT 71 | last-modified: Thu, 17 Oct 2019 07:18:26 GMT 72 | server: ECS (sed/58CC) 73 | vary: Accept-Encoding 74 | x-cache: HIT 75 | content-length: 1256 76 | 77 | 78 | 79 | 80 | Example Domain 81 | 82 | 83 | 84 | 85 | 112 | 113 | 114 | 115 |
116 |

Example Domain

117 |

This domain is for use in illustrative examples in documents. You may use this 118 | domain in literature without prior coordination or asking for permission.

119 |

More information...

120 |
121 | 122 | 123 | ``` 124 | 125 | ***source:*** *`~lagrev-nocfep`* \ 126 | ***context:*** *NONE* \ 127 | ***location:*** *TODO* 128 | 129 | ### Command: get docket for application 130 | 131 | For an application local to your own ship: 132 | ``` 133 | =docket -build-file /=landscape=/sur/docket/hoon 134 | =file -read %q [ship] [desk] [revision] /desk/docket-0 135 | (docket.docket file) 136 | ``` 137 | 138 | ***source:*** *`~dinleb-rambep`*\ 139 | ***context:*** *TODO*\ 140 | ***location:*** *TODO* 141 | 142 | ### Command: get latest hash of desk 143 | 144 | Example - get the latest hash of `%app` on ship `~sampel-palnet`: 145 | ``` 146 | -read %z ~sampel-palnet %app %da now / 147 | ``` 148 | 149 | ***source:*** *`~midden-fabler`*\ 150 | ***context:*** *TODO*\ 151 | ***location:*** *TODO* 152 | 153 | ### Command: poke an agent on another ship from dojo 154 | 155 | You can poke an agent on another ship from your dojo using the following format: 156 | `:[SHIP]/[AGENT] [MARK] [VASE]` 157 | 158 | Ex: `:~bacdun/faucet &faucet-action [%open 0x0 0xdead.beef]` 159 | 160 | ***source:*** *`~hodzod-walrus`, `rabsef-bicrym`*\ 161 | ***context:*** *NONE*\ 162 | ***location:*** *TODO* 163 | 164 | ### Command: print jammed noun to dojo 165 | 166 | 1. Save the jammed noun to a file 167 | 2. Commit the file to a desk in a ship 168 | 3. Load the file using Clay: `=j .^(@ %cx %/tang/jam)` 169 | 4. `cue` the loaded data: `(cue j)` 170 | 1. Might need to skip over the first 5 bytes if it's a newt-encoded jammed noun: `(cue (rsh [3 5] j))` 171 | 172 | ***source:*** *`~wicdev-wisryt`* \ 173 | ***context:*** *NONE* \ 174 | ***location:*** *TODO* 175 | 176 | ### Command: replace a broken vane on a ship 177 | 178 | 0. Shutdown ship (assuming it's bootable and running) 179 | 1. Generate replacement vane code 180 | 2. Boot a fake ship 181 | 3. Create a new desk in the fake ship 182 | ``` 183 | |new-desk %sandbox 184 | ``` 185 | 4. Mount the new desk to the external file system 186 | ``` 187 | |mount %sandbox 188 | ``` 189 | 5. Copy new vane code to the sandbox desk 190 | ``` 191 | cp /path/to/new-vane.hoon /path/to/fake/ship/sandbox/ 192 | ``` 193 | 6. Commit the changes to the fake ship 194 | ``` 195 | |commit %sandbox 196 | ``` 197 | 7. Create a jam file of the ovum used to replace the existing vane with the new code 198 | ``` 199 | .new-vane/jam [[%$ %arvo ~] %what [[%sys %vane %whatever ~] [%hoon .^(cord %cx /=sandbox=/new-vane/hoon)]] ~] 200 | ``` 201 | 8. Inject the ovum into the ship event log 202 | ``` 203 | /path/to/broken/ship/.run -I /path/to/fake/ship/.urb/put/new-vane.jam 204 | ``` 205 | OR 206 | ``` 207 | /path/to/urbit/binary -I /path/to/fake/ship/.urb/put/new-vane.jam /path/to/broken/ship 208 | ``` 209 | 210 | ***source:*** *`~finmep-lanteb`, `~master-morzod`*\ 211 | ***context:*** *NONE*\ 212 | ***location:*** *TODO* 213 | 214 | ### Command: what do the different command symbols mean? 215 | 216 | ``` 217 | +foo 'some' 'args' :: run %/gen/foo.hoon with args 218 | +foo!bar 'some' 'args' :: run /=foo=/gen/bar.hoon with args 219 | |foo 'some' 'args' :: poke %hood with output of %/gen/hood/foo.hoon with args 220 | :foo|bar 'some' 'args' :: poke %foo with output of %/gen/bar.hoon with args 221 | :foo ['some' 'args'] :: poke %foo with args as a %noun mark 222 | :foo &bar ['some' 'args'] :: poke %foo with args as a %bar mark 223 | :~zod/foo ['some' 'args'] :: poke %foo on ~zod with args as a %noun mark 224 | -foo 'some' 'args' :: run thread %foo with args 225 | -foo!bar 'some' 'args' :: run thread %bar in desk %foo with args 226 | &foo 'arg' :: cast arg to mark foo (in %/mar/foo.hoon) from noun 227 | ``` 228 | 229 | ***source:*** *`~tinnus-napbus`, `~watter-parter`, `~wicdev-wisryt`*\ 230 | ***context:*** *NONE*\ 231 | ***location:*** *TODO* 232 | 233 | ### Error: dojo stuck on `%dy-no-prompt` 234 | 235 | It's possible for the dojo (terminal) to get stuck on `%dy-no-prompt`, most likely due to an issue with a thread. If it 236 | does, pressing any key will result in a `%dy-edit-busy` message. `Ctrl+C` or forcefully stopping the ship then rebooting 237 | will do nothing. 238 | 239 | Disconnect the dojo from the thread by pressing `backspace` or `Ctrl+H`. Then, run `:spider|kill` in the dojo to stop 240 | whichever thread was responsible. Note that this command will kill all currently-running threads. Unfortunately, while 241 | there is a way to list the IDs of all currently-running threads (`:spider/tree`), there is no way to tie those IDs to a 242 | particular thread. 243 | 244 | ***source:*** *`~finmep-lanteb`, `~tinnus-napbus`*\ 245 | ***context:*** *NONE*\ 246 | ***location:*** https://urbit.org/using/os/shell#troubleshooting 247 | 248 | ### Error: "leak" messages in dojo after Vere crash 249 | 250 | This error usually appears as a list of messages like these: 251 | ``` 252 | 2024-02-06T21:39:12.119737301Z leak: 0x2c1caa190 mug=0 swept=1 253 | 2024-02-06T21:39:12.119737301Z size: B/24 254 | 2024-02-06T21:39:12.119737301Z data: [%spot [%sys %vane %ames %hoon 0] [3422 13] 3422 39] 255 | ``` 256 | 257 | These errors are caused by a `bail:meme` (OOM error) while copying results from a Nock computation to the home road. 258 | Running `pack` and `meld` commands then restarting should resolve the error on next boot. 259 | 260 | ***source:*** *`~master-morzod`*\ 261 | ***context:*** *NONE*\ 262 | ***location:*** *TODO* 263 | 264 | ### What does `|meld` do? 265 | 266 | `|meld` is a memory deduplication routine. 267 | 268 | Just like `|pack`, `|meld` will reduce the `free lists` down to 0. In addition, it will also attempt to reduce `sweep` 269 | by finding identical nouns and replacing the newer one with a reference to the older one. 270 | 271 | ***source:*** *`~wicdev-wisryt`*\ 272 | ***context:*** *[What does the output of `|mass` mean?](#what-does-the-output-of-mass-mean)*\ 273 | ***location:*** https://operators.urbit.org/manual/os/dojo-tools#meld 274 | 275 | ### What does `|pack` do? 276 | 277 | `|pack` is a memory defragmentation routine. 278 | 279 | It reduces the `free lists` down to 0 (though they'll quickly fill up to a few KB just from calling `|mass` to confirm 280 | this). 281 | 282 | ***source:*** *`~wicdev-wisryt`*\ 283 | ***context:*** *[What does the output of `|mass` mean?](#what-does-the-output-of-mass-mean)*\ 284 | ***location:*** https://operators.urbit.org/manual/os/dojo-tools#pack 285 | 286 | ### What does the output of `|mass` mean? 287 | 288 | ``` 289 | ~zod:dojo> |mass 290 | .....(truncated for brevity)..... 291 | total road stuff: B/376 292 | space profile: KB/12.936 293 | total marked: MB/176.019.156 294 | free lists: MB/35.927.760 295 | sweep: MB/176.019.156 296 | ``` 297 | 298 | `sweep` is the total amount of memory used. `free lists` is the total amount of fragmentation. If you add those two 299 | together, you get a number for which there is no name, but the difference between that number and the loom size is how 300 | much contiguous free memory your ship has available in its loom. This number is important because every event must be 301 | run in the contiguous free space of your ship's loom. The memory in the `free lists` can only be used at the end of the 302 | event, when the results of the event are being saved. 303 | 304 | The rest of the `|mass` output is used for drilling down to determine what exactly is using a ship's memory. 305 | 306 | ***source:*** *`~wicdev-wisryt`*\ 307 | ***context:*** *NONE*\ 308 | ***location:*** https://operators.urbit.org/manual/os/dojo-tools#mass 309 | 310 | ### What is the "base" hash in `+vats`? 311 | 312 | The "base" hash is a `tako`, a commit hash representing the merge-base of the local desk and its remote source. A commit 313 | hash is a concatenation of two 128-bit truncated SHA-256 hashes of a jammed noun of the head-tagged commit contents 314 | (see `+make-yaki` in `lull`). 315 | 316 | ***source:*** *`~master-morzod`*\ 317 | ***context:*** *NONE*\ 318 | ***location:*** *TODO* 319 | 320 | ### What is the "%cz" hash in `+vats`? 321 | 322 | The "%cz" hash is a Merkle-like hash of the contents of the desk (see `+content-hash` in `clay`). 323 | 324 | ***source:*** *`~master-morzod`*\ 325 | ***context:*** *NONE*\ 326 | ***location:*** *https://developers.urbit.org/reference/arvo/clay/scry#z---content-hash* 327 | -------------------------------------------------------------------------------- /urbit/README.md: -------------------------------------------------------------------------------- 1 | # Urbit 2 | 3 | This section contains miscellaneous tidbits about Urbit. 4 | 5 | ## Contents 6 | 7 | [Can I build a foreign desk using Clay?](#can-i-build-a-foreign-desk-using-clay) \ 8 | [Debug dashboard](#debug-dashboard) \ 9 | [How do I stop publishing an application?](#how-do-i-stop-publishing-an-application) \ 10 | [How do I update to a specific OTA?](#how-do-i-update-to-a-specific-ota) \ 11 | [How does Vere bootstrap the Hoon compiler?](#how-does-vere-bootstrap-the-hoon-compiler) \ 12 | [I messaged my ship from a comet and saw a breach notification. What happened?](#i-messaged-my-ship-from-a-comet-and-saw-a-breach-notification-what-happened) \ 13 | [Is a sequence of moves in an event guaranteed to terminate?](#is-a-sequence-of-moves-in-an-event-guaranteed-to-terminate) \ 14 | [Uninstalling vanes](#uninstalling-vanes) \ 15 | [What are the Hoon naming conventions?](#what-are-the-hoon-naming-conventions) \ 16 | [What is an event?](#what-is-an-event) \ 17 | [What is "remote scry"?](#what-is-remote-scry) \ 18 | [What is the boot sequence of a pill?](#what-is-the-boot-sequence-of-a-pill) \ 19 | [What is the relationship between Arvo, vanes, and agents?](#what-is-the-relationship-between-arvo-vanes-and-agents) \ 20 | [What is `tiny.hoon`?](#what-is-tinyhoon) 21 | 22 | ### Can I build a foreign desk using Clay? 23 | 24 | Yes. If a copy of the foreign desk is downloaded, then `%a` and `%x` `care`s to Clay work for foreign `beak`s. If the 25 | desk is not downloaded, you would need to manually scry with care `%v` first. 26 | 27 | ***source:*** *`~wicdev-wisryt`* \ 28 | ***context:*** https://developers.urbit.org/reference/arvo/clay/scry \ 29 | ***location:*** https://developers.urbit.org/reference/arvo/clay/scry 30 | 31 | ### Debug dashboard 32 | 33 | Every ship has a debugging dashboard available at `http://localhost:8080/~debug/`. It contains information about: 34 | - Incoming & outgoing app subscriptions 35 | - Running threads 36 | - Status of Ames communication with peers 37 | - Behn timers 38 | - Incoming Eyre HTTP requests 39 | 40 | ***source:*** *`~nospur-sontud`, `~wicdev-wisryt`* \ 41 | ***context:*** *NONE* \ 42 | ***location:*** *TODO* 43 | 44 | ### How do I stop publishing an application? 45 | 46 | `:treaty|unpublish %desk-name` 47 | 48 | ***source:*** *`~lagrev-nocfep`* \ 49 | ***context:*** *NONE* \ 50 | ***location:*** https://docs.urbit.org/userspace/apps/reference/dist/glob 51 | 52 | ### How do I update to a specific OTA? 53 | 54 | If a ship misses several OTAs, applying them all at once can become a blocking issue. The solution is to apply a single 55 | OTA at a time. 56 | 57 | Let's say we want to OTA specifically to the version of Arvo with `zuse` `%420`: 58 | 1. Determine the `cas` (version) of your sponsor's `%kids` desk that you need: 59 | 1. On the host run `.^(* %cw /=kids=)` to get a cell with the form `[X Y]`. Both are the `cas`: the first number is 60 | the numeric version of the current code, and the second is the date since which that version is active. 61 | - Alternatively, it's possible to do the same using `%base` on a ship that has successfully updated past the desired 62 | update: `.^(* %cw /=base=)` 63 | 2. Call `cat /=kids//sys/kelvin` with the numeric `cas` from above to see what the `zuse` version is: 64 | ``` 65 | > cat /=kids/130/sys/kelvin 66 | [%zuse 415] 67 | ``` 68 | 3. Walk the `cas` down until you find the lowest `cas` that returns the desired version of `zuse`. For example: 69 | ``` 70 | > cat /=kids/101/sys/kelvin 71 | [%zuse 419] 72 | > cat /=kids/100/sys/kelvin 73 | [%zuse 420] 74 | > cat /=kids/99/sys/kelvin 75 | [%zuse 420] 76 | > cat /=kids/98/sys/kelvin 77 | [%zuse 421] 78 | ``` 79 | In this case, the `cas` we need is `99`. 80 | 2. Upgrade to the specific version using the `cas` from above: 81 | ``` 82 | |merge %base %kids, =cas [%ud ], =gem %only-that 83 | ``` 84 | For example: 85 | ``` 86 | |merge %base ~litzod %kids, =cas [%ud 99], =gem %only-that 87 | ``` 88 | 89 | ***source:*** *`~dovsem-bornyl`*\ 90 | ***context:*** *TODO*\ 91 | ***location:*** *TODO* 92 | 93 | ### How does Vere bootstrap the Hoon compiler 94 | 95 | The first Hoon compiler was written in some other language. This compiler was then used to compile `hoon.hoon` to Nock. 96 | Vere is a Nock interpreter, and therefore is able to use the Nock version of `hoon.hoon` to henceforth further compile 97 | Hoon. 98 | 99 | When attempting to boot a ship, the third event in the solid or brass pill being used injects a precompiled Nock 100 | `hoon.hoon`. The fourth event is `hoon.hoon` as source, which is compiled by the Nock from the third event into new Nock 101 | which replaces the old Nock from the third event. 102 | 103 | ***source:*** *`~dozreg-toplud`*\ 104 | ***context:*** https://docs.urbit.org/glossary/pill \ 105 | ***location:*** *TODO* 106 | 107 | ### I messaged my ship from a comet and saw a breach notification. What happened? 108 | 109 | When a comet (or any ship) first boots, it needs to bootstrap a library of public keys for other ships registered via 110 | Azimuth. Originally, this meant reading the entire history of the ETH blockchain from the date that the Azimuth contract 111 | was published to the present day, recording who registered what keys when. 112 | 113 | In order to improve efficiency, a snapshot of the Azimuth state (and all keys registered at the time) was included in 114 | the solid pill, so that ships boot with a large collection of keys pre-installed. However, the ship still needs to catch 115 | up from that snapshot to the present day, in case new keys were since registered. 116 | 117 | Therefore, if your ship breached between the date of the snapshot and the current date, then as your comet is catching 118 | up to the present day, it will receive that breach notification as its updating. This is nothing about which to be 119 | concerned: it's just your comet catching up to the present state of the network. 120 | 121 | ***source:*** *`~finmep-lanteb`*\ 122 | ***context:*** *NONE*\ 123 | ***location:*** *TODO* 124 | 125 | ### Is a sequence of moves in an event guaranteed to terminate? 126 | 127 | No. For example, this is what a "resubscribe loop" is. This will eventually result in a crash due to stack overflow 128 | (usually a graceful crash: `bail %meme`). 129 | 130 | ***source:*** *`~wicdev-wisryt`* \ 131 | ***context:*** *NONE* \ 132 | ***location:*** *TODO* 133 | 134 | ### Uninstalling vanes 135 | 136 | Uninstalling a vane requires that any promises made by that vane regarding the namespace continue to be honored. Since a 137 | scry-handler is only a partial function, this means that in practice, the old state of the vane will still need to be 138 | maintained within the loom / snapshot. The simplest way to accomplish this is something along the lines of renaming the 139 | vane in any ships old enough to have had it to something like `%zzzz`, and adding some logic to the replacement vane 140 | along the lines of, "There used to be another vane here, but it's gone now. If you *really* need its old state for some 141 | reason, check at `%zzzz`." 142 | 143 | ***source:*** *`~master-morzod`* \ 144 | ***context:*** *NONE* \ 145 | ***location:*** *TODO* 146 | 147 | ### What are the Hoon naming conventions? 148 | 149 | In early Hoon, there were three naming conventions: 150 | - hyperlapidary 151 | - lapidary 152 | - freehand 153 | 154 | #### Hyperlapidary 155 | 156 | Only to be used for extremely regular and straightforward namespaces. Specifically designed to be as difficult as 157 | possible to refactor. Most often found in the most core sections of Arvo. 158 | - Arms must be gates or doors 159 | - Gate names should be 3 letters long and should aim to have mnemonic significance (e.g. `dec`) 160 | - Door names should be 2 letters long and should aim to resemble pronouns (e.g. `my`) 161 | - Conventional recursive structures have standard names 162 | - The head of a list is `i`, and the tail is `t` 163 | - The node in a tree is `n`, and the children are `l` and `r` respectively 164 | - Other structures should be short tuples, no wider than 5 elements 165 | - The legs are named `p`, `q`, `r`, `s`, and `t` in order of appearance 166 | - Variables and arguments should be named alphabetically with a single letter in order of appearance (e.g. `a`, `b`, 167 | `c`, etc.) 168 | 169 | #### Lapidary 170 | 171 | The ordinary style of Hoon. Most of Arvo is written in lapidary style. 172 | - Arms must be four letters long 173 | - They may or may not be English words that may or may not be cleverly relevant 174 | - All variables, arguments, attributes, etc. must be three letters long 175 | - Preferably consonant-vowel-consonant 176 | - If the same string is used more than once in a file, it should represent exactly the same concept 177 | - As if it were copy+pasted there 178 | 179 | #### Freehand 180 | 181 | God mode. Reserved for top-layer software, prototyping, and casual coding. 182 | - No rules. 183 | 184 | ***source:*** *`~sorreg-namtyv`*\ 185 | ***context:*** https://web.archive.org/web/20140424223310/http://urbit.org/doc/hoon/tut/7/ \ 186 | ***location:*** *TODO* 187 | 188 | ### What is an event? 189 | 190 | An event is one Unix move and all of its children. A Unix move is an external interaction with an Urbit ship that may 191 | modify its state. Examples include: 192 | - An Ames packet arrives 193 | - A Behn timer expires 194 | - The Clay files for a mounted desk are changed 195 | - A user types something in the dojo 196 | - An HTTP request comes in over Eyre 197 | 198 | All of these individual moves can set in motion additional moves: the Ames packet might contain a poke to a Gall agent which sets 199 | off a Clay recompilation. Thus, an event is the entire set of moves set in motion from the initial move. 200 | 201 | ***source:*** *`~wicdev-wisryt`*\ 202 | ***context:*** *NONE* \ 203 | ***location:*** *TODO* 204 | 205 | ### What is "remote scry"? 206 | 207 | "Remote scry" is an alternative networking protocol to Ames that is implemented in the Fine (fee-nay) vane. It allows 208 | ships to request data from foreign ships in a more efficient way and without affecting the state or event log of the 209 | foreign ship. 210 | 211 | Using Ames, every message affects the foreign ship state, even if only by updating sequence numbers. Using Fine, 212 | requests are read-only and globally referentially transparent (responses to identical requests are always identical). 213 | Since read requests do not affect state, they can be spawned in parallel threads and the results can be cached directly 214 | in the runtime, short-circuiting even the requirements that the request be handled by Fine in Nock. 215 | 216 | ***source:*** *`~wicdev-wisryt`*\ 217 | ***context:*** *NONE*\ 218 | ***location:*** *TODO* 219 | 220 | ### What is the boot sequence of a pill? 221 | 222 | A pill is three lists of ordered "events" (events isn't exactly correct, since they aren't all Arvo events). The first 223 | list contains boot-level events, the second list contains kernel events, and the third list contains userspace events 224 | (i.e. the pre-installed desks [e.g. `%base`, `%landscape`, etc.] and a Clay blob store). The events are structured in 225 | this way, so that developers have the option to side-load other events in-between the levels. 226 | 227 | During boot: 228 | - The first "event" is a lifecycle loop that defines how the following events should be injected. 229 | - The second "event" is a Nock formula to bootstrap a basic form of Arvo 230 | - The third "event": 231 | - For a "solid" pill, this is a precompiled Nock formula containing `hoon.hoon`, `arvo.hoon`, and the vanes which 232 | replaces the existing Arvo 233 | - For a "brass" pill, this is a precompiled Nock formula containing `hoon.hoon` 234 | - The fourth "event": 235 | - For a "solid" pill, there is no fourth event 236 | - For a "brass" pill, the `hoon.hoon` from the previous event is used to compile `arvo.hoon`, `lull.hoon`, 237 | `zuse.hoon`, and the vanes. The compiled Nock formulas replace the existing Arvo. 238 | 239 | ***source:*** *`~finmep-lanteb`*\ 240 | ***context:*** *NONE*\ 241 | ***location:*** *TODO* 242 | 243 | ### What is the relationship between Arvo, vanes, and agents? 244 | 245 | Arvo is just a stateful function as an OS: it's very simple and its capabilities are limited. Anything of value that you 246 | can do with Arvo, you must do through pluggable modules. This is what the vanes are. Vanes, however, are privileged, in 247 | the sense that they have the ability to create entries within the referentially transparent namespace. Therefore, 248 | another class of modules is necessary for arbitrary, pluggable, non-privileged behaviour. Thus: 249 | 250 | - Arvo: self-updating and privileged 251 | - Vanes: not self-updating but privileged 252 | - Agents: neither self-updating nor privileged 253 | 254 | For all intents and purposes, vanes *are* the kernel: a change to a vane is effectively a change to the kernel. Although 255 | they can technically be moved, installed, and uninstalled, it makes the most logical sense to ship Arvo and the vanes 256 | together. 257 | 258 | ***source:*** *`~master-morzod`*\ 259 | ***context:*** *NONE*\ 260 | ***location:*** *TODO* 261 | 262 | ### What is `tiny.hoon`? 263 | 264 | `tiny.hoon` is the standard library used by the ship implementing the naive rollup smart contract code. 265 | 266 | ***source:*** *`~ritpub-sipsyl`*\ 267 | ***context:*** *NONE*\ 268 | ***location:*** *TODO* 269 | -------------------------------------------------------------------------------- /development/runtime/README.md: -------------------------------------------------------------------------------- 1 | # Runtime 2 | 3 | This section contains information about runtime (Vere) development. 4 | 5 | ## Contents 6 | 7 | [Errors: `bail`ing on an inner road](#errors-bailing-on-an-inner-road) \ 8 | [Errors: How are deterministic errors handled?](#errors-how-are-deterministic-errors-handled) \ 9 | [Errors: How are non-deterministic errors handled?](#errors-how-are-non-deterministic-errors-handled) \ 10 | [Errors: Why do some deterministic error cases emit non-deterministic errors?](#errors-why-do-some-deterministic-error-cases-emit-non-deterministic-errors) \ 11 | [Hints: Is it desirable to have hint hooks that can be called before the dynamic hint formula is computed?](#hints-is-it-desirable-to-have-hint-hooks-that-can-be-called-before-the-dynamic-hint-formula-is-computed) \ 12 | [Hints: What should be done with malformed or unrecognized hints?](#hints-what-should-be-done-with-malformed-or-unrecognized-hints) \ 13 | [How does Vere track that a ship is "fake"?](#how-does-vere-track-that-a-ship-is-fake) \ 14 | [Jets: allocating in a jet](#jets-allocating-in-a-jet) \ 15 | [Jets: `bail` error code usage](#jets-bail-error-code-usage) \ 16 | [Jets: `lth`, `gth`, and derived code](#jets-lth-gth-and-derived-code) \ 17 | [Jets: Matching `?-` (wuthep) behaviour](#jets-matching---wuthep-behaviour) \ 18 | [Jets: When would a jet ever punt?](#jets-when-would-a-jet-ever-punt) \ 19 | [Known issues: Vere memory leak](#known-issues-vere-memory-leak) \ 20 | [Profiling: AddressSanitizer](#profiling-addresssanitizer) \ 21 | [Profiling: JSON Tracing](#profiling-json-tracing) \ 22 | [Profiling: Valgrind](#profiling-valgrind) \ 23 | [Testing: how should we test jets, since Vere and Arvo live in different repos?](#testing-how-should-we-test-jets-since-vere-and-arvo-live-in-different-repos) \ 24 | [What is the difference between the Vere interpreter and Vere daemon?](#what-is-the-difference-between-the-vere-interpreter-and-vere-daemon) \ 25 | [What is the directory structure of `urbit/pkg/urbit`?](#what-is-the-directory-structure-of-urbitpkgurbit) \ 26 | [What is the kelvin decrement process?](#what-is-the-kelvin-decrement-process) 27 | 28 | ### Errors: `bail`ing on an inner road 29 | 30 | U3 allocations take the road into account, therefore it's safe to `bail` on an inner road at any time. The road will be 31 | obliterated and all allocations safely deallocated. 32 | 33 | ***source:*** *`~master-morzod`*\ 34 | ***context:*** *TODO*\ 35 | ***location:*** *TODO* 36 | 37 | ### Errors: How are deterministic errors handled? 38 | 39 | When a deterministic error is encountered, the interpreter should perform clean-up tasks and then return to the previous 40 | virtualization layer. 41 | 42 | ***source:*** *`~wicdev-wisryt`*\ 43 | ***context:*** *TODO*\ 44 | ***location:*** *TODO* 45 | 46 | ### Errors: How are non-deterministic errors handled? 47 | 48 | In Vere, a non-deterministic error is handled by a `longjmp` back to the home road and the entire event being killed 49 | (note that this is [safe to do at any time](#errors-bailing-on-an-inner-road)). 50 | 51 | In Ares, a non-deterministic error is handled by killing the event thread and injecting the stack trace (note that this 52 | is safe, so long as no heap-allocation has been performed). 53 | 54 | ***source:*** *`~wicdev-wisryt`, `~ritpub-sipsyl`* \ 55 | ***context:*** *TODO*\ 56 | ***location:*** *TODO* 57 | 58 | ### Errors: Why do some deterministic error cases emit non-deterministic errors? 59 | 60 | Consider the case where a Nock formula attempts to create a list of `(2^64 + 1)` elements. There's nothing wrong with 61 | this formula, but no generally available computer of our day would be able to represent this list because it wouldn't 62 | be able to index it in the interpreter. Therefore, the failure is non-deterministic because the formula is correct but 63 | an external constraint of which Arvo is unaware is preventing it from being computed. 64 | 65 | ***source:*** *`~finmep-lanteb`, `~wicdev-wisryt`*\ 66 | ***context:*** *TODO*\ 67 | ***location:*** *TODO* 68 | 69 | ### Hints: Is it desirable to have hint hooks that can be called before the dynamic hint formula is computed? 70 | 71 | Yes, and they can even return shortcut results. However, the interpreter must still run the dynamic hint formula unless 72 | it has run this exact code (i.e. dynamic hint formula, nock formula, and subject) successfully once before (or unless 73 | it's trivially successful, e.g. Nock 1). Otherwise, an interpreter that knows how to handle the hint may incorrectly 74 | skip malformed hint formulas, whereas an interpreter that doesn't will correctly crash. 75 | 76 | ***source:*** *`~wicdev-wisryt`*\ 77 | ***context:*** *TODO*\ 78 | ***location:*** *TODO* 79 | 80 | ### Hints: What should be done with malformed or unrecognized hints? 81 | 82 | Formally, both malformed and unrecognized hints are ignored. It's possible that a hint in the Nock is not for the 83 | particular interpreter that has encountered it. Just because the current interpreter doesn't recognize the hint, or 84 | expects it to be formed differently, doesn't mean that all other interpreters do. Technically, the spec also allows the 85 | interpreter to kill the event, but in practice it's much better to just ignore the hint. 86 | 87 | There are two exceptions to the above: 88 | 1. A dynamic hint with a malformed hint Nock formula is invalid 89 | 2. A jet hint that produces an unhandled error in the jet is invalid 90 | 91 | ***source:*** *`~wicdev-wisryt`*\ 92 | ***context:*** *TODO*\ 93 | ***location:*** *TODO* 94 | 95 | ### How does Vere track that a ship is "fake"? 96 | 97 | A fake bit is persisted in the LMDB "META" database. This can be checked by the `fak_o` member in a `u3_pier` object, 98 | e.g. `c3y == pir_u->fak_o`. 99 | 100 | ***source:*** *`~master-morzod`*\ 101 | ***context:*** *TODO*\ 102 | ***location:*** *TODO* 103 | 104 | ### Jets: allocating in a jet 105 | 106 | Allocation outside of the loom is prohibited while in a jet. Only the on-loom allocation functions `u3a_malloc`, 107 | `u3a_realloc`, and `u3a_free` are okay. Vendored libraries that are used for jets either have functions that allocate 108 | removed, or have their allocators replaced. 109 | 110 | ***source:*** *`~dinleb-rambep`, `~master-morzod`*\ 111 | ***context:*** *TODO*\ 112 | ***location:*** *TODO* 113 | 114 | ### Jets: `bail` error code usage 115 | 116 | In a jet, `bail:exit` usage must exactly match the crash semantics of the Nock being jetted. All the other failures are 117 | semantically safe at any time. 118 | 119 | This requires understanding the crashes in the Nock specification, inspecting the Nock emitted by the code being jetted, 120 | and verifying the behaviour via untyped calls (test code fragments using the `slum` gate from the stdlib). 121 | 122 | ***source:*** *`~master-morzod`*\ 123 | ***context:*** *TODO*\ 124 | ***location:*** *TODO* 125 | 126 | ### Jets: `lth`, `gth`, and derived code 127 | 128 | ``` 129 | ++ lth 130 | ~/ %lth 131 | |= [a=@ b=@] 132 | ^- ? 133 | ?& !=(a b) 134 | |- 135 | ?| =(0 a) 136 | ?& !=(0 b) 137 | $(a (dec a), b (dec b)) 138 | == == == 139 | ``` 140 | 141 | Despite `lth` specifying that the gate requires its input to be two atoms, the type system doesn't actually exist at the 142 | Nock level. According to the Nock standard, the code should only fail when a Nock instruction attempts to use a cell as 143 | an atom. Therefore, attempting to call the Nock equivalent of the `lth` code with cells as one or both of the inputs 144 | should only fail at the instruction `$(a (dec a), b (dec b))`; resolving the Nock code for the decrement operation is 145 | the first point at which point cell input will be treated as an atom. 146 | 147 | However, this means that input where one of `a` or `b` is a cell, and the other is `0` should not bail: this is valid 148 | input for the above code. Similarly, if `a` and `b` are both cells but are identical, this is also valid input. 149 | Therefore, the jet for `lth` needs to take this into account, and does: 150 | ``` 151 | u3_noun 152 | u3wa_lth(u3_noun cor) 153 | { 154 | u3_noun a, b; 155 | 156 | if ( (c3n == u3r_mean(cor, u3x_sam_2, &a, u3x_sam_3, &b, 0)) 157 | || (c3n == u3ud(b) && 0 != a) 158 | || (c3n == u3ud(a) && 0 != b) ) 159 | { 160 | return u3m_bail(c3__exit); 161 | } 162 | else { 163 | return u3qa_lth(a, b); 164 | } 165 | } 166 | ``` 167 | 168 | The above also applies to `gth`. Jets for code that use `lth` or `gth` indirectly also need to keep this in mind. 169 | Examples include `lte`, `gte`, `min`, and `max`. 170 | 171 | ***source:*** *`~master-morzod`, `~fodwyt-ragful`*\ 172 | ***context:*** *TODO*\ 173 | ***location:*** *TODO* 174 | 175 | ### Jets: Matching `?-` (wuthep) behaviour 176 | 177 | When attempting to match `?-` behaviour in a jet using a `switch` statement, it's necessary to make sure that the jet 178 | captures the exact behaviour of the rune. `?-` is basically syntactic sugar for a sequence of `?:` runes with conditions 179 | and matching expressions that ends in a `~|(%mint-lost !!)` expression if none of the conditionals were met. 180 | 181 | The simplest way to do so is to include a `default` case which calls `u3m_bail(c3__fail)`. 182 | 183 | ***source:*** *`~master-morzod`*\ 184 | ***context:*** *NONE* \ 185 | ***location:*** *TODO* 186 | 187 | ### Jets: When would a jet ever punt? 188 | 189 | It's perfectly acceptable for a jet to only operate on a subset of a function's domain. Therefore, if it encountered 190 | input that it was unable to jet, it would punt back to Nock to evaluate the solution using the naive approach. 191 | 192 | Note that it is always safe to punt from within a jet. 193 | 194 | ***source:*** *`~wicdev-wisryt`*\ 195 | ***context:*** *TODO*\ 196 | ***location:*** *TODO* 197 | 198 | ### Known issues: Vere memory leak 199 | 200 | There is currently one known memory leak that occurs in Vere: when attempting to recover from an exception generated in 201 | the signal handler while on the home road. In practice, the particular issue that gets reported is `recover top: meme`, 202 | but that's not important: any exception generated in the signal handler on the home road will trigger the leak. 203 | 204 | ***source:*** *`~master-morzod`*\ 205 | ***context:*** *NONE* \ 206 | ***location:*** *TODO* 207 | 208 | ### Profiling: AddressSanitizer 209 | 210 | AddressSanitizer (ASan) is a tool for detecting various memory bugs in C/C++ code, such as buffer overflows, dangling 211 | pointers, and reading beyond allocation. Since the only current major implementation of Vere is written in C, it can be 212 | useful for identifying issues with the Urbit source code. 213 | 214 | ASan is built into the `clang` and `gcc` compilers. ASan is enabled during compilation for a binary by adding the 215 | `-fsanitize=address` flag to the build **and** linker flags. This used to be simple using the old dynamic Nix build 216 | system, but in the new static Bazel system it's a massive issue that has yet to be resolved. However, there 217 | [is a workaround](https://github.com/urbit/vere/issues/351) that allows ASan to be enabled. 218 | 219 | ***source:*** *`~finmep-lanteb`*\ 220 | ***context:*** *TODO*\ 221 | ***location:*** *TODO* 222 | 223 | ### Profiling: JSON Tracing 224 | 225 | Urbit is setup to create Chromium-tracing style JSON reports for profiling the wall-clock time spent in each function 226 | (an example is pictured below). 227 | 228 | ![JSON Tracing](https://raw.githubusercontent.com/ashelkovnykov/urbit-faq/master/development/runtime/resources/jtrace.png 'Urbit JSON Tracing Example') 229 | 230 | To enable JSON tracing reports, launch a ship with the `-j` option (e.g. `./urbit -j zod`). After you're done running 231 | the code that you want profiled, shut down the Urbit instance. The produced report will be located in the 232 | `.urb/put/trace` directory of the pier of the ship (e.g. `piers/zod/.urb/put/trace/0.json`). This report can be opened 233 | from the tracing interface (`chrome://tracing`) of any Chromium-based browser (Google Chrome, Brave, Vivaldi, etc.). 234 | 235 | ***source:*** *`~master-morzod`, `~ritpub-sipsyl`*\ 236 | ***context:*** https://www.chromium.org/developers/how-tos/trace-event-profiling-tool/ \ 237 | ***location:*** *TODO* 238 | 239 | ### Profiling: Valgrind 240 | 241 | Valgrind is a profiling tool for C/C++ debugging and memory leak detection. Since the only current major implementation 242 | of Vere is written in C, it can be useful for identifying issues with the Urbit source code. 243 | 244 | To run Vere with Valgrind requires the following process: 245 | 1. Install Valgrind 246 | 2. Make changes to Vere source 247 | 3. Drop `siz_i` on line `896` in `urbit/pkg/urbit/vere/disk.c` to `0x780000000` 248 | 4. Rebuild the Urbit binaries 249 | 250 | #### Callgrind 251 | 252 | To run the Callgrind tool, run the following command to launch your ship: 253 | ```shell 254 | valgrind --tool=callgrind --dump-instr=yes --simulate-cache=yes --collect-jumps=yes --log-file=callgrind.log ./urbit piers/zod/ 255 | ``` 256 | The Callgrind report produced must be visualized with another tool, most commonly 257 | [kcachegrind](https://kcachegrind.github.io/html/Home.html). 258 | 259 | Alternatively, to reduce the size of the report produced, you can recompile the binaries with manual calls to the 260 | instrumentation initialization and enable/disable macros: 261 | ``` 262 | CALLGRIND_START_INSTRUMENTATION 263 | CALLGRIND_TOGGLE_COLLECT 264 | ``` 265 | Then, the shell command to launch your ship would instead be: 266 | ```shell 267 | valgrind --tool=callgrind --dump-instr=yes --simulate-cache=yes --collect-jumps=yes --instr-atstart=no --collect-atstart=no --log-file=callgrind.log ./urbit piers/zod/ 268 | ``` 269 | Note that to do this, you will need to include the Callgrind macros in the Vere source, by adding the following files to 270 | the project: [valgrind.h](https://sourceware.org/git/?p=valgrind.git;a=blob_plain;f=include/valgrind.h.in;hb=HEAD) and 271 | [callgrind.h](https://sourceware.org/git/?p=valgrind.git;a=blob_plain;f=callgrind/callgrind.h;hb=HEAD). 272 | 273 | #### Memcheck 274 | 275 | To run the Memcheck tool, run the following command to launch your ship: 276 | ```shell 277 | valgrind --tool=memcheck --leak-check=yes --track-origins=yes --trace-children=yes --log-file=memcheck.log ./urbit piers/zod/ 278 | ``` 279 | 280 | ***source:*** *`~finmep-lanteb`, `~master-morzod`*\ 281 | ***context:*** https://valgrind.org \ 282 | ***location:*** *TODO* 283 | 284 | ### Testing: how should we test jets, since Vere and Arvo live in different repos? 285 | 286 | The jets tests run in both repos, under CPU_DEBUG and MEMORY_DEBUG. Both repos can refer to the specific version of the 287 | other repo that it wants. In `urbit/urbit`, the `vere-version` file specifies the binary against which it should be 288 | tested. In `urbit/vere`, the `solid_pill`, `ivory_pill`, and `urbit` settings in `WORKSPACE.bazel` specify the version 289 | of the kernel against which it should be tested. 290 | 291 | ***source:*** *`~wicdev-wisryt`*\ 292 | ***context:*** https://github.com/urbit/urbit, https://github.com/urbit/vere \ 293 | ***location:*** *TODO* 294 | 295 | ### What is the difference between the Vere interpreter and Vere daemon? 296 | 297 | Abstractly, an Urbit runtime needs to do 6 things: 298 | 1. represent nouns 299 | 2. persist arvo state 300 | 3. compute Nock 301 | 4. accelerate Nock 302 | 5. source events 303 | 6. persist events 304 | 305 | The Vere interpreter (also known as "worker" or "serf") does 1-4, whereas the Vere daemon (also known as "king") does 306 | all 6. 307 | 308 | There was a bunch of planned modularity for Vere that never materialized. The roles of the two processes are going to 309 | change in an upcoming update, and eventually the two binaries will likely be combined. 310 | 311 | ***source:*** *`~master-morzod`*\ 312 | ***context:*** https://github.com/urbit/urbit/pull/5596 \ 313 | ***location:*** *TODO* 314 | 315 | ### What is the directory structure of `urbit/pkg/urbit`? 316 | 317 | ``` 318 | bench: basic benchmarks (currently just de/serialization) 319 | daemon: entrypoint to the daemon/king process 320 | include: all headers 321 | jets: all jets 322 | noun: loom, noun representation and apis, checkpoints, nock/bytecode interpreter, jet dashboard 323 | tests: unit tests 324 | ur: off-loom noun representation and de/serialization 325 | vere: event scheduling, i/o drivers, event persistence, daemon/king implementation 326 | worker: entrypoint and implementation of the worker/serf process 327 | 328 | king = daemon + include + jets + noun + ur + vere 329 | serf = worker + include + jets + noun + ur 330 | ``` 331 | 332 | ***source:*** *`~master-morzod`*\ 333 | ***context:*** https://github.com/urbit/urbit/tree/master/pkg/urbit \ 334 | ***location:*** *TODO* 335 | 336 | ### What is the kelvin decrement process? 337 | 338 | For the purposes of example, we'll assume that we're decrementing the `hoon.hoon` from kelvin `10` to kelvin `9`. 339 | 340 | Note that any time a kelvin is decremented, all subordinate kelvins must also be decremented. The order is: `nock` -> 341 | `hoon` -> `arvo` -> `lull` -> `zuse`. Therefore, a decrement to the Nock kelvin would decrement all of the kelvins, 342 | whereas a decrement to the `lull.hoon` kelvin would only additionally decrement the `zuse.hoon` kelvin. 343 | 344 | 1. Copy jets for the new kelvin in `pkg/noun/jets/tree.c`, if decrementing Hoon kelvin 345 | 1. Practically, this means creating a new entry in `_d[]` for the new kelvin, which should look similar to the one 346 | above it: `{ "k9", 0, 0, _k9_d, no_hashes, 0, (u3j_core*) 9, 0 }` 347 | 2. It's unlikely that many of the jets changed between kelvins, so keep as much of the existing state as possible. 348 | For example, if only the jet for `dec` changed, then you would only need to define new `u3j_core` objects for 349 | `_k9_d` and `_9_one_d`: 350 | 1. ```u3j_core _k9_d[] = { { "one", 3, 0, _139_one_d, no_hashes }, {} };``` 351 | 2. ``` 352 | static u3j_core _139_one_d[] = { 353 | { "two", 3, 0, _10_two_d, no_hashes }, 354 | { "add", 7, _10_one_add_a, 0, no_hashes }, 355 | { "dec", 7, _9_one_add_a, 0, no_hashes }, 356 | { "div", 7, _10_one_add_a, 0, no_hashes }, 357 | ... 358 | {} 359 | }; 360 | ``` 361 | 3. Remove any old jets code from `pks/noun/jets/tree.c` (e.g. for kelvin `11` or higher) 362 | 2. Decrement the kelvin version(s) in `pkg/vere/pier.c`, object `u3_noun kel` 363 | 3. Decrement the Hoon kelvin version in `pkg/vere/serf.c`, if necessary 364 | 4. Compile the new binary 365 | 5. Check that the current Arvo still works on the binary 366 | 1. The binary must support Arvo both before and after the update 367 | 6. Decrement the kelvin version(s) in `hoon.hoon`, `arvo.hoon`, `lull.hoon`, and `zuse.hoon` - whichever are necessary 368 | 7. Compile a new ivory pill 369 | 8. Test the new ivory pill on the new binary 370 | 9. Upload the new ivory pill 371 | 372 | ***source:*** *`~finmep-lanteb`, `~wicdev-wisryt`*\ 373 | ***context:*** *TODO* \ 374 | ***location:*** *TODO* 375 | -------------------------------------------------------------------------------- /development/hoon/README.md: -------------------------------------------------------------------------------- 1 | # Hoon 2 | 3 | This section contains information about Hoon/Arvo development. 4 | 5 | ## Contents 6 | 7 | [Auras: how does Urbit time work?](#auras-how-does-urbit-time-work) \ 8 | [Best Practice: iterating over `cord`](#best-practice-iterating-over-cord) \ 9 | [Best Practice: disable `%spot` hints](#best-practice-disable-spot-hints) \ 10 | [Best Practice: helper cores in Gall agents](#best-practice-helper-cores-in-gall-agents) \ 11 | [Best Practice: when to use lead cores?](#best-practice-when-to-use-lead-cores) \ 12 | [Build: do I need to compile a new pill for changes to Urbit source code?](#build-do-i-need-to-compile-a-new-pill-for-changes-to-urbit-source-code) \ 13 | [Build: how do I test that `lib`/`sur` files are correct?](#build-how-do-i-test-that-libsur-files-are-correct) \ 14 | [Casting: auras from text](#casting-auras-from-text) \ 15 | [Casting: pull type out of gate output](#casting-pull-type-out-of-gate-output) \ 16 | [Casting: what is "normalization"?](#casting-what-is-normalization) \ 17 | [Error: `bail:meme` vs. `recover: dig: meme`](#error-bailmeme-vs-recover-dig-meme) \ 18 | [Error: `fire-core -fork-type.#! expected-fork-to-be-core fire-type`](#error-fire-core--fork-type-expected-fork-to-be-core-fire-type) \ 19 | [Error: `fuse-loop`](#error-fuse-loop) \ 20 | [Error: `mint-vain` or `-need.[i=@ t=it(@)] -have.%~` when using `list`](#error-mint-vain-or--needi-tit--have-when-using-list) \ 21 | [Functions: how does `+cap` work?](#functions-how-does-cap-work) \ 22 | [Functions: how does `+mas` work?](#functions-how-does-mas-work) \ 23 | [Functions: when should `homo` be used?](#functions-when-should-homo-be-used) \ 24 | [Gates: core vs. door, gate vs. trap](#gates-core-vs-door-gate-vs-trap) \ 25 | [Gates: gates with default values](#gates-gates-with-default-values) \ 26 | [Gates: is it bad to recurse on a wet gate?](#gates-is-it-bad-to-recurse-on-a-wet-gate) \ 27 | [Gates: naming a `|-` gate used as a loop](#gates-naming-a---gate-used-as-a-loop) \ 28 | [Jets: what are jet labels?](#jets-what-are-jet-labels) \ 29 | [Molds: bunt value of a mold](#molds-bunt-value-of-a-mold) \ 30 | [Operators: what does the `,` operator do?](#operators-what-does-the--operator-do) \ 31 | [Parsing: `cord` vs. `tape`](#parsing-cord-vs-tape) \ 32 | [Rendering: difference between `%palm` and `%rose` in `$tank`](#rendering-difference-between-palm-and-rose-in-tank) \ 33 | [Runes: centis (`%=`) vs. cencab (`%_`)](#runes-centis--vs-cencab-_) \ 34 | [Runes: how does bucket (`$^`) work?](#runes-how-does-bucket--work) \ 35 | [Runes: how does siglus (`~+`) work?](#runes-how-does-siglus--work) \ 36 | [Runes: how does sigtis (`~=`) work?](#runes-how-does-sigtis--work) \ 37 | [Runes: how does tissig (`=~`) work?](#runes-how-does-tissig--work) \ 38 | [Scry: can I virutalize scries?](#scry-can-i-virtualize-scries) \ 39 | [Scry: pass-through scry handler](#scry-pass-through-scry-handler) \ 40 | [Scry: what if the agent I'm scrying isn't running?](#scry-what-if-the-agent-im-scrying-isnt-running) \ 41 | [Scry: why does `.^` return `(unit (unit))`?](#scry-why-does--return-unit-unit) \ 42 | [Testing: disable authentication for HTTP](#testing-disable-authentication-for-http) \ 43 | [Types: inference via pattern matching](#types-inference-via-pattern-matching) \ 44 | [Types: recursive types](#types-recursive-types) \ 45 | [Vanes: Behn inserts src.bowl into timer path](#vanes-behn-inserts-srcbowl-into-timer-path) 46 | 47 | ### Auras: how does Urbit time work? 48 | 49 | Absolute Urbit time (`@da`) is a compound value: 50 | - The low 64 bits are `1/2^64` sub-second precision 51 | - The high 64 bits TAI64 with a different epoch (~292.5 billion years ago) 52 | 53 | ***source:*** *`~master-morzod`*\ 54 | ***context:*** https://github.com/urbit/urbit/blob/b8da026c5e0d7667c30f55298232da0f55004650/pkg/npm/api/lib/lib.ts \ 55 | ***location:*** *TODO* 56 | 57 | ### Best Practice: iterating over `cord` 58 | 59 | Using `rsh` to iterate over the byte data of a `cord` reallocates the entire remainder of the string. Instead, it's much 60 | more efficient to keep a "cursor" into the `cord` and slice bytes out of it using `cut`. Iterating over a `cord` using 61 | `cut` takes roughly 15% the amount of time as iterating over the same `cord` using `rsh`. 62 | 63 | ***source:*** *`~master-morzod`*\ 64 | ***context:*** https://github.com/urbit/urbit/pull/5456#issuecomment-980657399 \ 65 | ***location:*** *TODO* 66 | 67 | ### Best Practice: disable `%spot` hints 68 | 69 | `%spot` hints capture source locations, which get pushed onto the stack in case a trace needs to be generated. You can 70 | squeeze more performance out of code after it's confirmed to compile by disabling `%spot` hints by adding the `!.` rune 71 | to the beginning. 72 | 73 | ***source:*** *`~master-morzod`*\ 74 | ***context:*** *NONE* \ 75 | ***location:*** *TODO* 76 | 77 | ### Best Practice: helper cores in Gall agents 78 | 79 | If you have a helper core with a sample (a door) in your Gall agent, wrap it in another core and put the door under a 80 | named arm. Then, make an alias to point at that arm. This will prevent weird bugs whose root cause is accidentally 81 | invoking arms from the helper core without using the alias, and hence forgetting to set the sample of the core 82 | correctly. 83 | 84 | Ex: 85 | ``` 86 | ++ helper-core ~(. ^helper-core bowl) 87 | ... 88 | |% 89 | ++ helper-core 90 | |_ =bowl:gall 91 | ... 92 | == 93 | ``` 94 | 95 | ***source:*** *`~midlev-mindyr`*\ 96 | ***context:*** *NONE* \ 97 | ***location:*** *TODO* 98 | 99 | ### Best Practice: when to use lead cores? 100 | 101 | Lead cores are opaque, so you can't read their payload. This is very nice if you want to use the `=,` rune on a core 102 | which has a massive payload. For example, the `html` and `mimes:html` arms in `zuse` are lead cores so that users don't 103 | import multiple copies of the standard library into their subject. 104 | 105 | ***source:*** *`~master-morzod`*\ 106 | ***context:*** *NONE* \ 107 | ***location:*** *TODO* 108 | 109 | ### Build: do I need to compile a new pill for changes to Urbit source code? 110 | 111 | No, but depending on the size of the changes that you're making, to how many ships you need to apply the changes, and 112 | with how many people you need to share the changes, it may make development much faster. 113 | 114 | ***source:*** *`~master-morzod`*\ 115 | ***context:*** *TODO*\ 116 | ***location:*** *TODO* 117 | 118 | ### Build: how do I test that `lib`/`sur` files are correct? 119 | 120 | An easy way to check from inside an Urbit ship is to run the following commands: 121 | ``` 122 | > =l -build-file %/lib/my-file/hoon 123 | > (my-gate:l 'some' 'args') 124 | 125 | > =j -build-file /=/some-desk/=/lib/my-other-file/hoon 126 | > (my-other-gate:j 'some' 'args') 127 | ``` 128 | 129 | ***source:*** *`~timluc-miptev`*\ 130 | ***context:*** *NONE*\ 131 | ***location:*** https://operators.urbit.org/manual/os/dojo-tools#-build-file 132 | 133 | ### Casting: pull type out of gate output 134 | 135 | Consider the following situation: you have a `wet` `gate` `f` which takes another `gate` `g` as input. You want to 136 | explicitly cast the output of `f` to some type which is wholly or partially derived from the output of `g`. For example, 137 | let's say that `g` returns a `unit` of some type and `f` returns the value within that `unit`. The following code 138 | demonstrates how to do so: 139 | ``` 140 | |* g=$-(@ud (unit *)) 141 | ^- $_ 142 | =/ a (_*g) 143 | ?> ?= [~ *] a 144 | u.a 145 | =/ b (g 40) 146 | ?~ b 147 | 'a' 148 | u.b 149 | ``` 150 | Another, simplified method to do so for this particular case is: 151 | ``` 152 | |* g=$-(@ud (unit *)) 153 | ^- _(need *g) 154 | =/ b (g 40) 155 | ?~ b 156 | 'a' 157 | u.b 158 | ``` 159 | You can verify that both of these functions behave as intended by saving the above code as generators `gen1` and `gen2`, 160 | then using the "type spear" (`-:!>(l)`) as follows: 161 | ``` 162 | > =g |=(a=@ud ^-((unit @t) (some `@t`a))) 163 | > 164 | > =t +gen1 some 165 | > t 166 | 40 167 | > -:!>(t) 168 | #t/* 169 | > =t +gen1 g 170 | > t 171 | '(' 172 | > -:!>(t) 173 | #t/@t 174 | > 175 | > =t +gen2 some 176 | > t 177 | 40 178 | > -:!>(t) 179 | #t/* 180 | > =t +gen2 g 181 | > t 182 | '(' 183 | > -:!>(t) 184 | #t/@t 185 | ``` 186 | 187 | ***source:*** *`~finmep-lanteb`, `~sarpen-laplux`*\ 188 | ***context:*** *TODO*\ 189 | ***location:*** *TODO* 190 | 191 | ### Casting: auras from text 192 | 193 | When attempting to cast an aura from text stored as a `cord` (e.g. casting JSON data), it's necessary to use a 194 | formatting function such as `+slav` or `+slaw`. Just casting the input with kethep (`^-`) or `+soft` won't work, because 195 | auras aren't actual types, they're just hints about how atoms should be interpreted (i.e. the actual type is `atom`). 196 | Therefore, any aura can be trivially converted to any other aura, but the data might not retain the correct meaning 197 | (e.g. `^- @ud '42'` is `12.852`, not `42`). 198 | 199 | ***source:*** *`~dozreg-toplud`*\ 200 | ***context:*** https://docs.urbit.org/language/hoon/reference/stdlib/4m#slaw \ 201 | ***location:*** *TODO* 202 | 203 | ### Casting: what is "normalization"? 204 | 205 | Normalization is a two-step process performed by the compiler when a mold receives a value: 206 | 207 | 1. Make sure that the types match so that the mold can be applied to the value 208 | 2. Apply the faces of the mold to the value 209 | 210 | It's similar to the casting process in C: you can access the fields of an object through a pointer either directly (i.e. 211 | byte data) or by casting the pointer and using the member names. 212 | 213 | ***source:*** *`~timluc-miptev`*\ 214 | ***context:*** *NONE*\ 215 | ***location:*** *TODO* 216 | 217 | ### Error: `bail:meme` vs. `recover: dig: meme` 218 | 219 | Both of these errors are the same: OOM. However, `bail:meme` comes from Arvo itself whereas `recover: dig: meme` comes 220 | from a signal handler. In other words, the latter is detected by the runtime, which stops the computation and notifies 221 | the kernel which produces the former. 222 | 223 | ***source:*** *`~master-morzod`*\ 224 | ***context:*** *TODO*\ 225 | ***location:*** *TODO* 226 | 227 | ### Error: `fire-core -fork-type.#! expected-fork-to-be-core fire-type` 228 | 229 | This error has been known to appear in the following situations: 230 | - Attempting to use the head of a `list` as the key in a `map` without checking whether the `list` is `null` 231 | 232 | ***source:*** *`~finmep-lanteb`*\ 233 | ***context:*** *TODO*\ 234 | ***location:*** *TODO* 235 | 236 | ### Error: `fuse-loop` 237 | 238 | Any `_-loop` error in the compiler comes from expanding a `%hold` type, and then subsequently encountering that same 239 | `%hold` type again (i.e. a recursive definition error in the encountered type). 240 | 241 | `fuse` refers to `+fuse:ut`, which is used for type intersection during branch specialization in `?:`, `?=`, and any 242 | other rune that reduces to those two. If a pattern matches a wing, the type of the pattern is "added" to the type of the 243 | wing, otherwise it's "subtracted". Therefore, `fuse-loop` means the detection of an infinite loop in the type definition 244 | during branch specialization. 245 | 246 | The above is the exact, technical answer. At a practical level, it's a rather rare compiler error as, generally, type 247 | recursion issues tend to be caught by `fish-loop` (e.g. attempting to use `?=` with a recursive mold like `list`). A 248 | `fuse-loop` error implies that there is a recursive type error in the type of the wing of a `?=` / `?:`, as opposed to 249 | the type of the pattern (i.e. in the `~[1 2 3 4]` of `?=((list @) ~[1 2 3 4])`, as opposed to the `(list @)`). 250 | 251 | ***source:*** *`~master-morzod`*\ 252 | ***context:*** https://developers.urbit.org/guides/core/hoon-school/I-testing#fish-loop \ 253 | ***location:*** *TODO* 254 | 255 | ### Error: `mint-vain` or `-need.[i=@ t=it(@)] -have.%~` when using `list` 256 | 257 | This is known in Urbit as the TMI or "Too Much Information" problem: when the compiler knows too much about the type of 258 | a noun, to the point where it interferes with casting (usually when modifying the subject). Most often, this happens 259 | when working with a `list`, after checking whether it is `null`. 260 | 261 | The example below should illustrate how the compiler sees the types after using various Hoon runes, and the link below 262 | should provide additional context. 263 | 264 | ``` 265 | > =l1 `(list @)`[1 2 3 ~] 266 | > =l2 `(list @)`~ 267 | > =f1 |=(l=(list @) ?~(l -:!>(l) -:!>(l))) 268 | > =f2 |=(l=(list @) ?:(=(~ l) -:!>(l) -:!>(l))) 269 | > =f3 |=(l=(list @) ?:(?=(~ l) -:!>(l) -:!>(l))) 270 | > =f4 |=(l=(list @) ?:(.=(~ l) -:!>(l) -:!>(l))) 271 | > =f5 |=(l=(list @) ?:(?=(~ l) -:!>(l) =>(.(l `(list @)`l) -:!>(l)))) 272 | > (f1 l1) 273 | #t/[i=@ t=it(@)] 274 | > (f2 l1) 275 | #t/it(@) 276 | > (f3 l1) 277 | #t/[i=@ t=it(@)] 278 | > (f4 l1) 279 | #t/it(@) 280 | > (f5 l1) 281 | #t/it(@) 282 | > (f1 l2) 283 | #t/%~ 284 | > (f2 l2) 285 | #t/it(@) 286 | > (f3 l2) 287 | #t/%~ 288 | > (f4 l2) 289 | #t/it(@) 290 | > (f5 l2) 291 | #t/%~ 292 | ``` 293 | 294 | ***source:*** *`~norsyr-torryn`, `~palfun-foslup`, `~sorreg-namtyv`*\ 295 | ***context:*** 296 | - https://github.com/urbit/arvo/issues/1024 297 | - https://web.archive.org/web/20140424223310/http://urbit.org/doc/hoon/tut/7/ 298 | 299 | ***location:*** *TODO* 300 | 301 | ### Functions: how does `+cap` work? 302 | 303 | First, see the section below on how `+mas` works. Then, it's a similar idea: 304 | `+cap` determines whether the index is within the head or the tail of a tree, 305 | therefore (using the bit labeling from the `+mas` example), all we care about is 306 | bit `a`, representing the first "turn". If `a` is `0`, then the index is in the 307 | head; otherwise, it's in the tail. 308 | 309 | ***source:*** *`~finmep-lanteb`* \ 310 | ***context:*** NONE \ 311 | ***location:*** https://docs.urbit.org/language/hoon/reference/stdlib/1b#cap 312 | 313 | ### Functions: how does `+mas` work? 314 | 315 | If the bits of the atom passed in to `+mas` as input look like the following: 316 | ``` 317 | 1abc...z 318 | ``` 319 | (ex: `13 = 0b1101`, therefore `a = 1`, `b = 0`, and `c = 1`), then what `+mas` 320 | is really doing is returning the atom made from bits: 321 | ``` 322 | 1bc...z 323 | ``` 324 | (ex: `(mas 13) = 5 = 0b101`) 325 | 326 | This is because the bits of an atom representing an index encode how to walk 327 | through the tree in the following way: 328 | ``` 329 | 1101 330 | ^^^^ 331 | |||| 332 | RIGHT --+┘|| 333 | LEFT --+-┘| 334 | RIGHT --+--┘ 335 | DONE --┘ 336 | ``` 337 | 338 | Since `+mas` is computing the relative index within either the head or tail, we 339 | don't care about the first "turn" being right or left, so we drop the bit 340 | entirely. 341 | 342 | ***source:*** *`~finmep-lanteb`* \ 343 | ***context:*** NONE \ 344 | ***location:*** https://docs.urbit.org/language/hoon/reference/stdlib/1b#mas 345 | 346 | ### Functions: when should `homo` be used? 347 | 348 | `+homo` is occasionally necessary to unify the types of disparate inputs when working simultaneously with `$list`s and 349 | wet gates, typically when dealing with "casually constructed" `$list`s (e.g. `(limo %a %b %c)`): 350 | ``` 351 | > =l (limo [%abc %def 123 ~]) 352 | > ? l 353 | ^#3.?([i=%abc t=#3] ^#2.?([i=%def t=#2] it(@ud))) 354 | [i=%abc t=[i=%def t=~[123]]] 355 | > ? (homo l) 356 | it(?(%abc %def @ud)) 357 | ~[%abc %def 123] 358 | ``` 359 | 360 | ***source:*** *`~master-morzod`* \ 361 | ***context:*** https://docs.urbit.org/courses/hoon-school/R-metals#wet-gates \ 362 | ***location:*** https://docs.urbit.org/language/hoon/reference/stdlib/2b#homo 363 | 364 | ### Gates: core vs. door, gate vs. trap 365 | 366 | Just as a door is just a core with a sample, a gate is just a trap with a sample. This can be confirmed by the following 367 | code: 368 | ``` 369 | > =foo |=(a=@ a) 370 | > =bar =|(@ |.(+6)) 371 | > (foo 5) 372 | 5 373 | > (bar 5) 374 | 5 375 | ``` 376 | 377 | ***source:*** *`~master-morzod`*\ 378 | ***context:*** 379 | - https://developers.urbit.org/reference/glossary/core 380 | - https://developers.urbit.org/reference/glossary/door 381 | - https://developers.urbit.org/reference/glossary/gate 382 | - https://developers.urbit.org/reference/glossary/trap 383 | 384 | ***location:*** https://developers.urbit.org/guides/core/hoon-school/F-cores#repeating-yourself-using-a-trap 385 | 386 | ### Gates: gates with default values 387 | 388 | The `|:` rune produces gates which have default values for the sample. If the sample has multiple values, but you only 389 | want to override one (or just not all) of them, you need to call the gate using the `%*` rune: 390 | ``` 391 | > =f |: [a=1 b=2] (add a b) 392 | > %* $ f b 4 == 393 | 5 394 | ``` 395 | 396 | ***source:*** *`~tinnus-napbus`* \ 397 | ***context:*** https://developers.urbit.org/reference/hoon/rune/cen#-centar \ 398 | ***location:*** *TODO* 399 | 400 | ### Gates: is it bad to recurse on a wet gate? 401 | 402 | Wet gate recursion is almost never desirable: the `+mull` check is re-performed at each call site, meaning that each 403 | iteration could potentially have a different type (producing heterogeneous `list`s, for example). 404 | 405 | ***source:*** *`~master-morzod`* \ 406 | ***context:*** *NONE*\ 407 | ***location:*** https://docs.urbit.org/courses/hoon-school/R-metals 408 | 409 | ### Gates: naming a `|-` gate used as a loop 410 | 411 | If you want to assign a name to a gate created by `|-` that's used as a loop (for example, if you have nested loops), 412 | the best way to do so is to use create an alias with the `=*` rune: 413 | ``` 414 | ... 415 | |- 416 | =* outer-loop $ 417 | ... 418 | |- 419 | ... 420 | ``` 421 | 422 | ***source:*** *`~palfun-foslup`, `~rovnys-ricfer`*\ 423 | ***context:*** *NONE*\ 424 | ***location:*** *TODO* 425 | 426 | ### Jets: what are jet labels? 427 | 428 | Jet labels are the list passed as argument `r` to sigcen (`~%`) (typically `~`). They are hooks to Hoon code that the 429 | jet may use as a part of its implementation. An example for why one may wish to use these hooks is that the jet may have 430 | only a partial implementation, so it wants to call the raw Hoon code for a subset of its domain. Another example is that 431 | the jet may only perform pre / post processing on input that is computed by raw Hoon. 432 | 433 | ***source:*** *`finmep-lanteb`* \ 434 | ***context:*** https://docs.urbit.org/language/hoon/reference/rune/sig#-sigcen \ 435 | ***location:*** *TODO* 436 | 437 | ### Molds: bunt value of a mold 438 | 439 | By default, molds bunt to their final type case, e.g.: 440 | 441 | ``` 442 | > =mold $? @ux [%b @ub] [%d @ud] == 443 | > *mold 444 | [%d 0] 445 | ``` 446 | 447 | ***source:*** *`master-morzod`* \ 448 | ***context:*** https://docs.urbit.org/courses/hoon-school/E-types \ 449 | ***location:*** [Mold](https://docs.urbit.org/glossary/mold), [Bunt](https://docs.urbit.org/glossary/bunt) 450 | 451 | ### Operators: what does the `,` operator do? 452 | 453 | When used as a wing, `,` strips faces from legs: 454 | ``` 455 | > =x a=[b=1 c=2] 456 | > ,.x 457 | [b=1 c=2] 458 | ``` 459 | 460 | However, on its own `,` is irregular syntax for `^:`. This will switch the compiler into "structure" mode, thereby 461 | producing a mold: 462 | ``` 463 | > ,@ 464 | <1.mek [* [our=@p now=@da eny=@uvJ] <17.fkq 33.ehb 14.dyd 53.vlb 77.lrt 232.oiq 51.qbt 123.zao 46.hgz 1.pnw %140>]> 465 | > (,@) 466 | 0 467 | ``` 468 | 469 | ***source:*** *`~finmep-lanteb`, `~sidnym-ladrut`, `~tinnus-napbus`*\ 470 | ***context:*** https://developers.urbit.org/guides/core/hoon-school/L-struct#structure-mode \ 471 | ***location:*** https://developers.urbit.org/reference/hoon/limbs/wing 472 | 473 | ### Parsing: `cord` vs. `tape` 474 | 475 | Parsing a `cord` instead of a `tape` trades memory pressure for call overhead. Traversing a contiguously allocated cons 476 | list (the ideal case for a `tape`) is pretty quick. However, operations on a `cord` can be jetted much more effectively. 477 | 478 | ***source:*** *`~master-morzod`*\ 479 | ***context:*** https://github.com/urbit/urbit/pull/5456 \ 480 | ***location:*** *TODO* 481 | 482 | ### Rendering: difference between `%palm` and `%rose` in `$tank` 483 | 484 | In short, `$tank` is massively over-engineered and heavily biased to rendering Hoon. `%palm`s are rendered in 485 | "queenside" format (with backstpes after a new line), whereas `%rose`s are rendered in "kingside" format. This is best 486 | illustrated with these examples: 487 | ``` 488 | ((slog (crip (of-wall:format (~(win re `tank`[%palm ["," "[" "!" "]"] ~['abc' 'def' 'ghi']]) 0 10))) ~) ~) 489 | [ abc 490 | def 491 | ghi 492 | 493 | > ((slog (crip (of-wall:format (~(win re `tank`[%rose ["," "[" "]"] ~['abc' 'def' 'ghi']]) 0 10))) ~) ~) 494 | [ abc 495 | def 496 | ghi 497 | ] 498 | ``` 499 | 500 | ***source:*** *`~master-morzod`, `~sidnym-ladrut`*\ 501 | ***context:*** *TODO*\ 502 | ***location:*** *TODO* 503 | 504 | ### Runes: centis (`%=`) vs. cencab (`%_`) 505 | 506 | When modifying the value of a wing, you often modify the type in subtle ways. For example, consider this example from 507 | the STL, which recurses over a `list` using barhep (`|-`): 508 | ``` 509 | 1 ++ lent :: length 510 | 2 ~/ %lent 511 | 3 |= a=(list) 512 | 4 ^- @ 513 | 5 =+ b=0 514 | 6 |- 515 | 7 ?~ a b 516 | 8 $(a t.a, b +(b)) 517 | ``` 518 | 519 | Line `8` recurses by calling the irregular form of `%=`. Why not `%_`? After line `7`, we know that `a` is non-null. 520 | Therefore, the type of `a` is no longer `list`, but actually `lest`. However, for the recursion to function correctly, 521 | we need the type of `a` to be `list` when we loop back to line `6`. Therefore, we need to slightly alter the type at the 522 | same time as altering the value. 523 | 524 | ***source:*** *`~master-morzod`*\ 525 | ***context:*** *TODO* \ 526 | ***location:*** *TODO* 527 | 528 | ### Runes: how does bucket (`$^`) work? 529 | 530 | `$^` works exactly like `$@`, but with input of the form `[^ [^ ^]]` instead of form `[@ ^]`. 531 | 532 | ***source:*** *`~finmep-lanteb`*\ 533 | ***context:*** *TODO* \ 534 | ***location:*** *TODO* 535 | 536 | ### Runes: how does siglus (`~+`) work? 537 | 538 | `~+` caches the product of a `[formula subject]` pair until the end of the current "road" (see context). 539 | 540 | Each Arvo event runs on the kernel's "outer" road. However, the kernel may launch "inner" roads for virtualizing 541 | processes such as userspace agents. Each road has its own cache, which is cleared when the road is removed at the 542 | conclusion of its task. 543 | 544 | ***source:*** *`~master-morzod`, `~ritpub-sipsyl`, `~tinnus-napbus`*\ 545 | ***context:*** https://github.com/urbit/urbit/blob/master/doc/spec/u3.md#u3-the-road-model \ 546 | ***location:*** https://developers.urbit.org/reference/hoon/rune/sig#-siglus 547 | 548 | ### Runes: how does sigtis (`~=`) work? 549 | 550 | If the values of the two arguments `p` and `q` are identical, `q` is discarded and replaced with a reference to `p`. 551 | This allows Hoon to save space when pinning a new result to the subject by referencing existing data. 552 | 553 | ***source:*** *`~timluc-miptev`*\ 554 | ***context:*** *TODO*\ 555 | ***location:*** https://developers.urbit.org/reference/hoon/rune/sig#-sigtis 556 | 557 | ### Runes: how does tissig (`=~`) work? 558 | 559 | `=~` is a convenient way to compose multiple cores together, though it's rare to see outside of core code (particularly 560 | kernel vanes). Typically, Clay will automatically compose cores for you when building a `.hoon` file in userspace. 561 | However, this needs to be done explicitly in kernel space, so Eyre and Iris use `=~` for this purpose. 562 | 563 | A non-idiomatic use case is to use `=~` to pass a single object through a chain of expressions; an example of this can 564 | be found in Ames. 565 | 566 | ***source:*** *`~watter-parter`*\ 567 | ***context:*** *TODO*\ 568 | ***location:*** https://developers.urbit.org/reference/hoon/rune/tis#-tissig 569 | 570 | ### Scry: can I virtualize scries? 571 | 572 | There is no way to virtualize scries directly. Doing so requires the help of a vane, as giving userspace the option to 573 | virtualize scries directly would expose kernel state and violate referential transparency. 574 | 575 | ***source:*** *`~master-morzod`*\ 576 | ***context:*** *TODO*\ 577 | ***location:*** *TODO* 578 | 579 | ### Scry: pass-through scry handler 580 | 581 | Each level of virtualization in Hoon requires an explicit "scry handler": a gate that exists outside the scope of the 582 | virtualization that can handle the virtualized scry operations. However, typically the virtualization call does not 583 | require unique scry handling logic and would prefer to use the parent scry handler. Thus was born the "pass-through scry 584 | handler": a scry handler that just forwards scries to the scry handler being used at *its* level of virtualization: 585 | ``` 586 | |=(a=^ .*(a [%12 [%0 2] %0 3])) 587 | ``` 588 | 589 | In some cases, this scry handler needs to be passed as compiled Nock. The compiled Nock and the formula to compile it 590 | are below: 591 | ``` 592 | > .* 0 != => ~ |=(a=^ ``.*(a [%12 [%0 2] %0 3])) 593 | [[[1 0] [1 0] 2 [0 6] 1 12 [0 2] 0 3] [0 0] 0] 594 | ``` 595 | 596 | ***source:*** *`~master-morzod`*\ 597 | ***context:*** *TODO*\ 598 | ***location:*** *TODO* 599 | 600 | ### Scry: what if the agent I'm scrying isn't running? 601 | 602 | A scry for an agent that doesn't exist will always crash. However, it's possible to check if an agent is installed and 603 | running using the `u` `care` of a Gall scry: 604 | ``` 605 | .^(? %gu /our/agent/now) 606 | ``` 607 | 608 | ***source:*** *`~tinnus-napbus`*\ 609 | ***context:*** *TODO*\ 610 | ***location:*** *TODO* 611 | 612 | ### Scry: why does `.^` return `(unit (unit))`? 613 | 614 | - `~`: "This scry path might have existed in the past, or it may exist in the future, but it doesn't exist for the given 615 | `cas`." 616 | - `[~ ~]`: "This scry path doesn't exist, has never existed, and will never exist." 617 | - `[~ ~ *]`: "This scry path exists and returned the given data." 618 | 619 | ***source:*** *`~tinnus-napbus`*\ 620 | ***context:*** *TODO*\ 621 | ***location:*** *TODO* 622 | 623 | ### Testing: disable authentication for HTTP 624 | 625 | To disable `+code` authentication when accessing fake ships through the browser, set `authenticated` to always be true 626 | in 627 | [Eyre](https://github.com/urbit/urbit/blob/1b0a7770685afea8abd2f93fea4982e4f8071f0a/pkg/arvo/sys/vane/eyre.hoon#L826). 628 | 629 | ***source:*** *`~dinleb-rambep`*\ 630 | ***context:*** *TODO*\ 631 | ***location:*** *TODO* 632 | 633 | ### Types: inference via pattern matching 634 | 635 | The Hoon compiler is only able to make type inferences through positive pattern matches. Consider the following code 636 | example: 637 | 638 | ``` 639 | > =x `(unit @)`[~ 5] 640 | > ?: ?=(^ x) u.x 0 641 | 5 642 | > ?: !?=(@ x) u.x 0 643 | -find.u.x 644 | find-fork 645 | ``` 646 | 647 | ***source:*** *`~dozreg-toplud`* \ 648 | ***context:*** *TODO* \ 649 | ***location:*** *TODO* 650 | 651 | ### Types: recursive types 652 | 653 | Recursive types almost always need to have a bunt specified using the `$~` (bucsig) rune to be compiled correctly: 654 | ``` 655 | |% 656 | +$ recurse 657 | $~ [*@t ~] 658 | $: name=@t 659 | subs=(map @t recurse) 660 | == 661 | -- 662 | ``` 663 | 664 | ***source:*** *`~rovnys-ricfer`* \ 665 | ***context:*** *TODO* \ 666 | ***location:*** *TODO* 667 | 668 | ### Vanes: Behn inserts `src.bowl` into timer path 669 | 670 | When you send a `%wait` task to Behn, it will automatically prepend `src.bowl` to the timer's path. This can be an issue 671 | if you want to cancel timers, as you'll need to track which ship set the timer. 672 | 673 | ***source:*** *`~hodzod-walrus`*\ 674 | ***context:*** *TODO*\ 675 | ***location:*** https://developers.urbit.org/reference/arvo/behn/tasks 676 | --------------------------------------------------------------------------------