on master help sign in
Merge branch 'master' into alias-not-macro
commit7d8e171d4b5ccad93e852c8c2cc1855721c44404 authorpoprox <51337059+drbeefsupreme@users.noreply.github.com> dateThu 16 Sep 2021, 16:05 (5 years ago) parent63657302337d13
70 files changed+2151−1105
.npmrc+1 components/Contact.js+1−7 components/EventPreview.js+17−12 components/Footer.js+1−6 components/Markdown.js+3−1 components/Section.js+2−2 components/Snippets.js+44 components/TableOfContents.js+5−2 components/TwoUp.js+12 components/icons/Cross.js+11 content/blog/haleek-maul-interview.md+71 content/blog/urbithost-interview.md+2−2 content/docs/arvo/clay/examples.md+26−58 content/docs/arvo/concepts/_index.md+4 content/docs/arvo/concepts/scry.md+77 content/docs/arvo/dill/_index.md+16 content/docs/arvo/dill/data-types.md+187 content/docs/arvo/dill/dill-api.md−366 content/docs/arvo/dill/dill.md+35−18 content/docs/arvo/dill/scry.md+45 content/docs/arvo/dill/tasks.md+342 content/docs/arvo/eyre/tasks.md+15−15 content/docs/arvo/iris/tasks.md+3−3 content/docs/arvo/jael/scry.md+15−15 content/docs/arvo/jael/tasks.md+15−15 content/docs/hoon/reference/cheat-sheet.md+3−13 content/docs/hoon/reference/rune/zap.md+1−1 content/docs/hoon/reference/style.md+108−108 content/docs/userspace/graph-store/sample-application-overview.md+369 content/docs/userspace/overview.md+5 content/docs/userspace/threads/basics/summary.md+3−3 content/docs/userspace/threads/examples/child-thread.md+33−33 content/docs/userspace/threads/examples/get-json.md+5−4 content/docs/userspace/threads/examples/main-loop.md+8−8 content/docs/userspace/threads/examples/poke-agent.md+2−2 content/docs/userspace/threads/examples/scry.md+10−10 content/docs/userspace/threads/examples/take-fact.md+2−2 content/docs/userspace/threads/gall/poke-thread.md+8−9 content/docs/userspace/threads/gall/start-thread.md+9−9 content/docs/userspace/threads/gall/stop-thread.md+8−8 content/docs/userspace/threads/gall/take-facts.md+8−8 content/docs/userspace/threads/gall/take-result.md+9−9 content/docs/userspace/threads/http-api.md+1−1 content/docs/userspace/threads/reference.md+5−13 content/events/2021-09-16-developer-call-azimuth-scaled.md+46 content/events/2021-09-24-urbit-outside.md+23 content/events/2021-10-15-assembly.md+26 content/getting-started/planet.md+4−2 content/grants/a-collaborative-web-mapping-system-for-urbit.md → content/grants/a-network-enabled-spatial-database-and-demonstration-app.md+26−21 content/grants/ethereum-abi-parser.md+1 content/grants/matrices-in-hoon.md+1−1 content/grants/~home-urbit.md+120 content/using/id/guide-to-breaches.md+1−3 content/using/os/getting-started.md+3−3 content/using/os/ship-troubleshooting.md+2−2 lib/constants.js+55−1 lib/glossary.js+112−112 lib/hooks.js+22 lib/lib.js+1−1 next.config.js+1 package-lock.json+5 package.json+2−1 pages/blog/[slug].js+9−18 pages/events.js+5−19 pages/events/[slug].js+37−90 pages/grants.js+10−3 pages/grants/[slug].js+2−1 pages/index.js+66−37 styles/globals.css+22−25 styles/markdown.css+2−2
.npmrcnew file+1
@@ -0,0 +1 @@
1+unsafe-perm = true
components/Contact.js+1−7
@@ -79,13 +79,7 @@ export default function Contact({ emphasize }) {
7979 Github
8080 </a>
8181 </h4>
82− <h4 className="mt-6 text-wall-500">
83− Ask questions in our{" "}
84− <a className={linkText} href={contact.discord}>
85− Discord
86− </a>
87− </h4>
88− <h4 className="mt-6 text-wall-500 flex items-baseline flex-wrap">
82+ <h4 className="mt-4 text-wall-500 flex items-baseline flex-wrap">
8983 Boot Urbit and join{" "}
9084 <div className="md:ml-2 rounded-lg flex items-stretch">
9185 <code
components/EventPreview.js+17−12
@@ -1,6 +1,11 @@
11import Link from "next/link";
22import { DateTime } from "luxon";
3−import { ShowOrHide, ReadableList, Person } from "../components/Snippets";
3+import {
4+ ShowOrHide,
5+ ReadableList,
6+ Person,
7+ DateRange,
8+} from "../components/Snippets";
49import {
510 generateDisplayDate,
611 generateRealtimeDate,
@@ -11,7 +16,7 @@ import {
1116
1217export default function EventPreview({ event, className, big }) {
1318 // Event tiles have a 'dark mode' used when their background images are dark and white text is needed for legibility.
14− const grayText = event?.dark ? "text-midWhite" : "text-wall-400";
19+ const grayText = event?.dark ? "text-washedWhite" : "text-wall-400";
1520 const blackText = event?.dark ? "text-white" : "text-wall-600";
1621
1722 const starts = generateDisplayDate(event.starts, event.timezone);
@@ -23,7 +28,9 @@ export default function EventPreview({ event, className, big }) {
2328 generateRealtimeDate(event.starts) > DateTime.now() && !inFuture;
2429
2530 return (
26− <div className={`cursor-pointer aspect-w-5 aspect-h-4 ${className}`}>
31+ <div
32+ className={`cursor-pointer aspect-w-4 aspect-h-5 md:aspect-w-5 md:aspect-h-4 ${className}`}
33+ >
2734 <div
2835 key={event.slug}
2936 className={`bg-wall-100 rounded-xl bg-cover bg-center bg-no-repeat `}
@@ -38,15 +45,13 @@ export default function EventPreview({ event, className, big }) {
3845 big ? "justify-center" : ""
3946 } flex-col h-full`}
4047 >
41− <h4 className={`${blackText}`}>{event.title}</h4>
42− <p className={grayText}>{formatDate(starts)}</p>
43− <p className={grayText}>
44− {formatTime(starts)}
45− {typeof event.ends !== "undefined"
46− ? ` to ${formatTime(ends)}`
47− : null}
48− {" " + formatTimeZone(starts)}
49− </p>
48+ <h3 className={`${blackText} mb-2`}>{event.title}</h3>
49+ <p className={`${blackText} mb-2`}>{event.location}</p>
50+ <DateRange
51+ starts={starts}
52+ ends={ends}
53+ className={`${grayText} type-sub`}
54+ />
5055 </div>
5156
5257 <div className="absolute p-6 left-0 bottom-0 w-full pr-32">
components/Footer.js+1−6
@@ -5,7 +5,7 @@ import { contact } from "../lib/constants";
55
66export default function Footer() {
77 return (
8− <footer className="bg-wall-100 w-full mt-48 flex justify-center">
8+ <footer className="bg-wall-100 mt-20 w-full flex justify-center">
99 <SingleColumn>
1010 <Section short className="flex flex-row flex-wrap">
1111 <div className="w-1/2 md:w-1/3 flex flex-col flex-shrink">
@@ -126,11 +126,6 @@ export default function Footer() {
126126 <a className="type-bold text-wall-500">Dev Mailing List</a>
127127 </div>
128128 </Link>
129− <Link href="https://discord.gg/CfSKKaB2ep">
130− <div>
131− <a className="mt-2 type-bold text-wall-500">Support Discord</a>
132− </div>
133− </Link>
134129 <Link href="https://github.com/urbit/azimuth">
135130 <div>
136131 <a className="mt-2 type-bold text-wall-500">Governance</a>
components/Markdown.js+3−1
@@ -38,7 +38,9 @@ export default async function Markdown({ post }) {
3838 const result = await remark()
3939 .use(html, options)
4040 .use(gfm)
41− .use(remarkprism)
41+ .use(remarkprism, {
42+ plugins: ["show-invisibles"],
43+ })
4244 .use(slug)
4345 .use(heading)
4446 .process(post.content);
components/Section.js+2−2
@@ -8,8 +8,8 @@ export default function Section({
88 narrow = false,
99}) {
1010 const spacing = classnames({
11− "py-4 md:py-6 lg:py-8": short,
12− "py-8 md:py-10 lg:py-20": !short,
11+ "py-8": short,
12+ "py-20": !short,
1313 "layout-narrow": narrow,
1414 layout: !narrow,
1515 });
components/Snippets.js+44
@@ -1,3 +1,12 @@
1+import {
2+ getAllEvents,
3+ generateDisplayDate,
4+ generateRealtimeDate,
5+ formatDate,
6+ formatTime,
7+ formatTimeZone,
8+} from "../lib/lib";
9+
110export function Name({ children, className }) {
211 return <b className={`font-normal ${className || ""}`}>{children}</b>;
312}
@@ -61,3 +70,38 @@ export function ShowOrHide({ children, condition }) {
6170 }
6271 return null;
6372}
73+
74+export function DateRange({ starts, ends, className, short }) {
75+ // For events which have no end datetime
76+ if (!ends.isValid) {
77+ return (
78+ <div>
79+ <p className={className}>
80+ {`${formatDate(starts)} • ${formatTime(starts)} ${formatTimeZone(
81+ starts
82+ )}`}
83+ </p>
84+ </div>
85+ );
86+ }
87+ // For events which start and end on the same day
88+ if (starts.hasSame(ends, "day")) {
89+ return (
90+ <div>
91+ <p className={className}>
92+ {`${formatDate(starts)} • ${formatTime(starts)} to ${formatTime(
93+ ends
94+ )} ${formatTimeZone(starts)}`}
95+ </p>
96+ </div>
97+ );
98+ }
99+ // For multi-day events
100+ return (
101+ <div>
102+ <p className={className}>{`${starts.toFormat(
103+ "cccc, LLLL d"
104+ )} to ${formatDate(ends)}`}</p>
105+ </div>
106+ );
107+}
components/TableOfContents.js+5−2
@@ -52,9 +52,12 @@ const useHeadingsData = (noh3s) => {
5252
5353const Headings = ({ headings, activeId }) => (
5454 <ul>
55− {headings.map((heading) => (
55+ {headings.map((heading, index) => (
5656 <li key={heading.id}>
57− <a className="font-semibold" href={`#${heading.id}`}>
57+ <a
58+ className={index === 0 ? "font-bold" : "font-medium"}
59+ href={`#${heading.id}`}
60+ >
5861 {heading.title}
5962 </a>
6063 {heading.items.length > 0 && (
components/TwoUp.jsnew file+12
@@ -0,0 +1,12 @@
1+export default function TwoUp({ children, className = "" }) {
2+ return (
3+ <div className={`flex flex-wrap w-full ${className}`}>
4+ <div className="w-full md:w-1/2 pr-0 pb-8 md:pr-4">
5+ {children[0] || null}
6+ </div>
7+ <div className="w-full md:w-1/2 pl-0 pb-8 md:pl-4">
8+ {children[1] || null}
9+ </div>
10+ </div>
11+ );
12+}
components/icons/Cross.jsnew file+11
@@ -0,0 +1,11 @@
1+export default function Cross(props) {
2+ return (
3+ <svg {...props} viewBox="0 0 12 12">
4+ <path
5+ fillRule="evenodd"
6+ clipRule="evenodd"
7+ d="M0.292893 10.2942C-0.0976311 10.6847 -0.0976311 11.3179 0.292893 11.7084C0.683417 12.0989 1.31658 12.0989 1.70711 11.7084L6.00063 7.41487L10.2948 11.7091C10.6853 12.0996 11.3185 12.0996 11.709 11.7091C12.0996 11.3185 12.0996 10.6854 11.709 10.2949L7.41484 6.00066L11.7084 1.70711C12.0989 1.31658 12.0989 0.683417 11.7084 0.292894C11.3179 -0.0976312 10.6847 -0.0976312 10.2942 0.292894L6.00063 4.58645L1.70775 0.293571C1.31723 -0.0969534 0.684061 -0.0969534 0.293536 0.293571C-0.0969879 0.684095 -0.0969879 1.31726 0.293536 1.70778L4.58641 6.00066L0.292893 10.2942Z"
8+ />
9+ </svg>
10+ );
11+}
content/blog/haleek-maul-interview.mdnew file+71
@@ -0,0 +1,71 @@
1++++
2+title = "NFTs, Urbit IDs, and Communities w/ Haleek Maul"
3+date = "2021-09-09"
4+description = "An interview with the founder of Holdersland"
5+[extra]
6+author = "Matt"
7+ship = "~tirwyd-sarmes"
8+image = "https://media.urbit.org/site/posts/essays/haleek_sigil.jpg"
9++++
10+
11+![](https://media.urbit.org/site/posts/essays/haleek_sigil.jpg)
12+
13+*Check out the Holdersland x Urbit collection: [holders.land/urbit](https://holders.land/urbit)*
14+
15+We sat down recently with Haleek Maul to discuss the current state of NFTs and the future of Urbit IDs and digital communities.
16+
17+Are Urbit IDs NFTs? Sure, they're ERC-721 non-fungible tokens which you can buy on OpenSea—but to be clear, we made them way back before the word "NFT" had entered mainstream consciousness. An Urbit ID is a number that is used to generate both a phonetic name and an avatar (what we call [sigils](https://urbit.org/blog/creating-sigils)), but it is so much more than your average NFT—it is a key to an entire digital world.
18+
19+We built Urbit IDs for digital communities and the people they’re made of. An Urbit ID is a decentralized and pseudonymous identity perfect for your collective, commune, squad, team, or DAO. Unlike most addresses or social identities, you own your Urbit ID like you own your crypto. Your Discord handle might be free for now, but as the saying goes, if it’s free then you’re the product. On Urbit your ID is actually yours and you pay for it up front. You can log into third party apps or software without ever relinquishing control of your data. What is yours stays with you. IDs themselves can be owned collectively—there are already DAOs on Urbit who jointly own stars and galaxies.
20+
21+Urbit IDs are evolving and generative. They’re generative because some IDs spawn additional NFTs which themselves can be bought and sold. For instance, each star can spawn 65,535 planets, and each galaxy can spawn 255 stars. The network itself is evolving, meaning that the network can be upgraded via a majority vote by galaxy holders. Urbit IDs and the Ubit network itself are inherently dynamic.
22+
23+At Urbit, we support communities of all kinds. We recently granted Holdersland ten Urbit IDs: four stars and six planets to be auctioned off. Haleek Maul (his Urbit ID is `~fosmex-hocmel`) will be creating custom artwork for each of the sigils. The proceeds of the auction will help support Holdersland in bringing blockchain technologies to the Caribbean. The auction will go live on September 16th, 2021 on [holders.land/urbit](https://holders.land/urbit).
24+
25+Without further ado, here’s Haleek Maul.
26+
27+---
28+
29+**Tell us a little bit about yourself and Holdersland.**
30+
31+Sure. My name is Haleek Maul and I’m a multi-disciplinary artist. I was born in America and came to Barbados when I was three years old. I’ve been making music since 2007. I put out my first debut project in 2012 and since then it has been a process of creating art from the stuff flowing through my mind.
32+
33+Holdersland is a digital extension of my artistic project—a way of creating change in the world around me. When I was younger, there weren’t many artists like me coming from Barbados. I was into a lot of niche electronic and club music and felt alienated from the larger conversation. Holdersland is a way to help creative people in the Caribbean connect with like-minded people around the world, especially using new technologies like blockchain. So I founded Holdersland in 2020 when I left my record label after four years. I felt like music institutions was archaic and didn’t feed into my other artistic practices. I wanted to create an institution that captured the full creative practice—all the way from creating to selling. Most importantly, I wanted to get artists from Barbados and the Caribbean involved in global conversations around art and technology.
34+
35+**What exactly is Holdersland?**
36+
37+It’s a company centered around Caribbean intellectual property. We’re trying to help people get connected with new web3 technologies, especially artists. Holdersland is an aggregator that helps connects those who are interested in new technology with those those who actually develop it.
38+
39+For example, there is a lot of math that underlies music. Part of our project is to extract these mathematical principles and see how they can help generate new forms of art that can be widely distributed. So we’re working to build our own projects and work with users to adopt some of these new technologies. It’s a community of people centered around these values. We plan to implement voting, governance, and incentive structures into the community soon.
40+
41+**What do you think is the link between your two main interests: music and technology?**
42+
43+I’ve always been interested in technology. Anyone who has made music in the last twenty years has had to get involved with tech. Especially if you’re a producer. There are many young kids who are downloading music software and messing around with it, and in the process becoming knowledgeable about music production and design. Science and math live right beside art.
44+
45+I was also into web development when I was a teenager and was very interested in how the internet worked. This led into the work I’m doing with Holdersland. Technology, more often than not, is concerned with collaboration and communication. But in order to develop new technologies we often need to talk about nitty-gritty details. This requires detailed knowledge of particular technologies.
46+
47+**What is the great hope or promise with new decentralized technologies like blockchain or Urbit?**
48+
49+They lower the barrier to communication and trade. Especially for countries like Barbados, we don’t have access to the world market. But now with these networks, and assets like NFTs, we can engage in commerce without borders. It allows new entrants to join the market.
50+
51+It also allows new financial relationships and funding structures to emerge. Rather than have a few people contribute $20k, you can have hundreds of people contribute $100. Once again, this lowers the entry for people to invest is early-stage projects. This changes the way that companies build and ship products.
52+
53+Brain drain is when skilled workers move abroad for more profitable employment. These new technologies allow native Carribeans to stay where they are while earning money. Using these new technologies, the Carribean can prosper while not losing its unique character and cultural identity. Crypto is permanently going to change the distribution of wealth and culture around the world.
54+
55+Discord is a fine community solution, but it’s not ideal. There’s an endless array of plugins and dependencies that makes Discord unstable. However, it is a good mixture of an office, a storefront, and a club: I can do my work, sell assets, and just have fun. Communities are important. People naturally want to be in communities, we just need a better solution than Discord. Especially one that’s crypto-native and allows for commerce. Urbit is a one-stop shop that could fill that gap.
56+
57+**What is the collaboration between Holdersland and Urbit that’s happening this week?**
58+
59+I’m producing some custom sigil artwork for a set of stars and planets. Both the Urbit assets and artwork will be auctioned off together. The proceeds from the event will go towards creating a community music studio here in Barbados and technology education around the island.
60+
61+A lot of people here in Barbados don’t even understand what crypto is. A lot of the work being done around this project is to raise awareness about the potentials of this new technology. I want this to be a general springboard for the Caribbean to gain greater technological literacy. That literacy is an important step for lower-income people to drastically change their situation.
62+
63+**What are you looking forward to talking about on the New World Energy panel at Assembly 2021?**
64+
65+I want to talk about how to migrate communities onto Urbit. How to use Urbit as a new platform for niche cultures found around the current internet. Also the great strength and power provided by Urbit’s digital identities.
66+
67+NFTs can be very powerful—there are a lot of possible applications of a unique digital asset. It’s already dissolved geographical boundaries which will help countries to export their intellectual products freely. Countries need first to gain knowledge around these technologies and their economies will follow.
68+
69+**Thanks for chatting!**
70+
71+See you at Assembly 2021!
content/blog/urbithost-interview.md+2−2
@@ -5,10 +5,10 @@ description = "Interview with the founder of UrbitHost ~lavlyn-litmeg"
55[extra]
66author = "Matt"
77ship = "~tirwyd-sarmes"
8−image = "https://media.urbit.org/site/posts/essays/urbithost.png"
8+image = "https://media.urbit.org/site/posts/essays/nasa-star-cluster.jpg"
99+++
1010
11−![](https://media.urbit.org/site/posts/essays/urbithost.png)
11+![](https://media.urbit.org/site/posts/essays/nasa-star-cluster.jpg)
1212
1313**An Interview with UrbitHost**
1414
content/docs/arvo/clay/examples.md+26−58
@@ -4,38 +4,6 @@ weight = 7
44template = "doc.html"
55+++
66
7−# Contents
8−
9−- [Introduction](#introduction)
10−- [%warp](#warp) - Read and Subscribe.
11− - [%sing](#sing) - Read a file or directory.
12− - [%next](#next) - Subscribe for the next change to a file or directory.
13− - [%mult](#mult) - Subscribe for the next change to a set of files and/or directories.
14− - [%many](#many) - Track changes to a `desk` for the specified range of revisions.
15− - [Cancel Subscription](#cancel-subscription)
16−- [%info](#info) - Write and Modify.
17− - [%ins](#ins) - Add a file.
18− - [%del](#del) - Delete a file.
19− - [%mut](#mut) - Change a file.
20− - [Multiple Changes](#multiple-changes) - Change multiple files.
21−- [Manage Mounts](#manage-mounts)
22− - [%boat](#boat) - List mounts.
23− - [%mont](#mont) - Mount something.
24− - [%ogre](#ogre) - Unmount something.
25− - [%dirk](#dirk) - Commit changes.
26−- [Merge Desks](#merge-desks)
27− - [%merg](#merg)
28−- [Permissions](#permissions)
29− - [%perm](#perm) - Set file permissions.
30− - [%cred](#cred) - Add permission group.
31− - [%crew](#crew) - Get permission groups.
32− - [%crow](#crow) - Get group usage.
33−- [Foreign Ships](#foreign-ships)
34− - [%warp](#warp-remote) - Read a file on a foreign ship.
35− - [%merg](#merg-remote) - Merge from a foreign `desk`.
36−
37−# Introduction
38−
397This document contains a number of examples of interacting with Clay using its various `task`s. Sections correspond to the general details in the [API Reference](/docs/arvo/clay/tasks) document.
408
419Most examples will either use `|pass` to just send a `task` or the following thread to send a `task` and take the resulting `gift`. You can save the following thread to the `ted` directory of your `%home` `desk`:
@@ -61,11 +29,11 @@ Most examples will either use `|pass` to just send a `task` or the following thr
6129(pure:m !>(~))
6230```
6331
64−# `%warp`
32+## `%warp`
6533
6634See the [Read and Subscribe](/docs/arvo/clay/tasks#warp---read-and-track) section of the [API Reference](/docs/arvo/clay/tasks) document for general details.
6735
68−## `%sing`
36+### `%sing`
6937
7038Here we'll look at reading files by passing Clay a `%warp` `task` with a `%sing` `rave` and receiving a `%writ` `gift` containing the data in response.
7139
@@ -115,7 +83,7 @@ Here's a breakdown of the `task` we sent:
11583
11684![%sing diagram](https://media.urbit.org/docs/arvo/clay/sing.png "%sing diagram")
11785
118−## `%next`
86+### `%next`
11987
12088Here we'll look at subscribing to the next version of a file by passing Clay a `%warp` `task` with a `%next` `rave` and receiving a `%writ` `gift` when the file changes.
12189
@@ -151,7 +119,7 @@ Here's a breakdown of the task we sent:
151119
152120![%next diagram](https://media.urbit.org/docs/arvo/clay/next.png "%next diagram")
153121
154−## `%mult`
122+### `%mult`
155123
156124Here we'll look at subscribing to the next version of multiple files by passing Clay a `%warp` `task` with a `%mult` `rave` and receiving a `%wris` `gift` when any of the files change.
157125
@@ -210,7 +178,7 @@ Here's a breakdown of the `task` we sent:
210178
211179![subscribe mult diagram](https://media.urbit.org/docs/arvo/clay/sub-mult.png "subscribe mult diagram")
212180
213−## `%many`
181+### `%many`
214182
215183Here we'll look at subscribing to a range of changes to a `desk` by passing Clay a `%warp` `task` with a `%many` `rave` and receiving `%writ` `gift`s when changes occur.
216184
@@ -304,7 +272,7 @@ Here's a breakdown of the `task` we sent:
304272
305273![subscribe many diagram](https://media.urbit.org/docs/arvo/clay/sub-many.png "subscribe many diagram")
306274
307−## Cancel Subscription
275+### Cancel Subscription
308276
309277Here we'll look at cancelling a subscription by sending Clay a `%warp` `task` with a null `(unit rave)` in the `riff`.
310278
@@ -348,11 +316,11 @@ Here's a breakdown of the `task` we sent:
348316
349317![cancel subscription diagram](https://media.urbit.org/docs/arvo/clay/stop-sub.png "cancel subscription diagram")
350318
351−# `%info`
319+## `%info`
352320
353321See the [Write and Modify](/docs/arvo/clay/tasks#write-and-modify) section of the [API Reference](/docs/arvo/clay/tasks) document for general details.
354322
355−## `%ins`
323+### `%ins`
356324
357325Here we'll look at adding a file by sending Clay a `%info` `task` containing a `%ins` `miso`.
358326
@@ -371,7 +339,7 @@ Here's a breakdown of the `task` we sent:
371339
372340![%ins diagram](https://media.urbit.org/docs/arvo/clay/ins.png)
373341
374−## `%del`
342+### `%del`
375343
376344Here we'll look at deleting a file by sending Clay a `%info` `task` containing a `%del` `miso`.
377345
@@ -388,11 +356,11 @@ Here's a breakdown of the `task` we sent:
388356
389357![%del diagram](https://media.urbit.org/docs/arvo/clay/del.png)
390358
391−## `%mut`
359+### `%mut`
392360
393361Identical to the [%ins](#ins) example, just replace `%ins` with `%mut`.
394362
395−## Multiple Changes
363+### Multiple Changes
396364
397365Here we'll look at changing multiple files in one request by sending Clay a `%info` `task` containing multiple `miso` in the `soba`.
398366
@@ -433,11 +401,11 @@ Save to `ted/multi-change.hoon`, `|commit %home`, and run:
433401- /~zod/home/38/baz/txt
434402```
435403
436−# Manage Mounts
404+## Manage Mounts
437405
438406See the [Manage Mounts](/docs/arvo/clay/tasks#manage-mounts) section of the [API Reference](/docs/arvo/clay/tasks) document for general details.
439407
440−## `%boat`
408+### `%boat`
441409
442410Here we'll look at requesting the list of existing mount points on a ship by sending Clay a `%boat` `task` and receiving a `%hill` `gift`.
443411
@@ -448,7 +416,7 @@ Using the `send-task-take-gift.hoon` thread, let's make such a request:
448416[%hill p=~[%home]]
449417```
450418
451−## `%mont`
419+### `%mont`
452420
453421Here we'll look at mounting `desk`s, directories and files to unix by sending Clay a `%mont` `task`.
454422
@@ -487,7 +455,7 @@ Let's try mounting a subdirectory and a single folder:
487455
488456If you look in your pier you'll now see a `gen` folder with the contents of `/gen` and a `hi.hoon` file by itself. Notice how the file extension has been automatically added.
489457
490−## `%ogre`
458+### `%ogre`
491459
492460Here we'll look at unmounting `desk`s, directories and files by sending Clay a `%ogre` `task`.
493461
@@ -518,7 +486,7 @@ If we specify a non-existent mount point it will fail with an error printed to t
518486
519487If we give it an unmounted `beam` it will not print an error but still won't work.
520488
521−## `%dirk`
489+### `%dirk`
522490
523491Here we'll look at committing changed files by sending Clay a `%dirk` `task`.
524492
@@ -535,11 +503,11 @@ Clay will print the changed files to the dojo with a leading `+`, `-` or `:` to
535503
536504If you have the same `desk` mounted to multiple points, a committed change in one mount will also update the others.
537505
538−# Merge Desks
506+## Merge Desks
539507
540508See the [Merge Desks](/docs/arvo/clay/tasks#merge-desks) section of the [API Reference](/docs/arvo/clay/tasks) document for general details.
541509
542−## `%merg`
510+### `%merg`
543511
544512Here we'll look at merging `desk`s by sending Clay a `%merg` `task` and receiving a `%mere` `gift` in response.
545513
@@ -610,11 +578,11 @@ Now change the text in the `foo.txt` in the `%home` `desk`, hit backspace to det
610578
611579You can also specify it by revision number or label.
612580
613−# Permissions
581+## Permissions
614582
615583See the [Permissions](/docs/arvo/clay/tasks#permissions) section of the [API Reference](/docs/arvo/clay/tasks) document for general details.
616584
617−## `%perm`
585+### `%perm`
618586
619587Here we'll look at setting permissions by sending Clay a `%perm` `task`.
620588
@@ -694,7 +662,7 @@ Here's a breakdown of a `%perm` task:
694662
695663![perm diagram](https://media.urbit.org/docs/arvo/clay/perm-diagram.png)
696664
697−## `%cred`
665+### `%cred`
698666
699667Here we'll look at creating a permission group by sending Clay a `%cred` `task`.
700668
@@ -706,7 +674,7 @@ Let's create a group called `'foo'` with a few ships:
706674
707675We'll check it with the next kind of `task`: [%crew](#crew).
708676
709−## `%crew`
677+### `%crew`
710678
711679Here we'll look at retrieving permission groups by sending Clay a `%crew` `task` and receiving a `%cruz` `gift` in response.
712680
@@ -717,7 +685,7 @@ Let's check, using the `send-task-take-gift.hoon` thread, for the permission gro
717685[%cruz cez={[p=~.foo q={~nec ~bud ~wes ~zod ~sev}]}]
718686```
719687
720−## `%crow`
688+### `%crow`
721689
722690Here we'll look at retrieving a list of all files and directories in all `desk`s which have permissions set for a group by sending Clay a `%crow` `task` and receiving a `%croz` `gift` in response.
723691
@@ -749,11 +717,11 @@ Now we'll use the `send-task-take-gift.hoon` thread to try `%crow`:
749717]
750718```
751719
752−# Foreign Ships
720+## Foreign Ships
753721
754722See the [Foreign Ships](/docs/arvo/clay/tasks#foreign-ships) section of the [API Reference](/docs/arvo/clay/tasks) document for general details.
755723
756−## `%warp` - Remote
724+### `%warp` - Remote
757725
758726Here we'll look at reading files on a foreign ship by sending Clay a `%warp` `task` with a foreign ship in the `wer` field and receiving a `%writ` `gift` in response.
759727
@@ -816,7 +784,7 @@ call: failed
816784/sys/vane/clay/hoon:<[1.365 48].[1.365 50]>
817785```
818786
819−## `%merg` - Remote
787+### `%merg` - Remote
820788
821789To merge a foreign `desk` into a local one, you just send Clay a `%merg` `task` (as you would for a local merge) and specify the foreign ship in the `her` field. For an example, see the [%merg](#merg) section.
822790
content/docs/arvo/concepts/_index.md+4
@@ -8,6 +8,10 @@ insert_anchor_links = "right"
88
99Other concepts related to the way Arvo works.
1010
11+## [Scries](/docs/arvo/concepts/scries)
12+
13+Details of performing scries.
14+
1115## [Subscriptions](/docs/arvo/concepts/subscriptions)
1216
1317Urbit relies heavily on subscriptions throughout the stack. We strongly prefer reactive data flow compared to querying or polling, which is why for example our equivalent to git natively supports subscriptions to its data.
content/docs/arvo/concepts/scry.mdnew file+77
@@ -0,0 +1,77 @@
1++++
2+title = "Scries"
3+weight = 40
4+template = "doc.html"
5++++
6+
7+### What is a scry?
8+
9+A scry is a read-only request to Arvo's global namespace.
10+
11+Vanes and agents define _scry endpoints_ which allow one to request data from their respective states. The endpoints can process the data in any way before returning it, but they cannot alter the actual state - scries can _only_ read, not modify.
12+
13+### Why scry?
14+
15+The subject available in something like a Gall agent or thread contains a great many functions and structures from the standard library as well as `zuse` and `lull`, but it does not include any of the actual data stored elsewhere in the ship. All it has is its own state, a `bowl` and any `card`s it has been passed. Ordinarily, in order to access such data, one would need to `%poke` or `%watch` other agents, or `%pass` `task`s to vanes, then wait for a response. Arvo's scry system is the one exception to this; it allows direct retrieval of data from other vanes or agents in situ, from any context, without any of the normal messaging rigmarole.
16+
17+### How do I scry?
18+
19+Scries are performed exclusively with the dotket rune: `.^`
20+
21+For details of its usage, see the [dotket](/docs/hoon/reference/rune/dot#-dotket) section of the Nock rune documentation. In addition to the dotket documentation, below is a colour-coded diagram breaking down the structure of a dotket scry expression with some additional notes.
22+
23+![Scry diagram](https://media.urbit.org/docs/arvo/scry-diagram-v2.svg)
24+
25+One further note on `care`s (which can sometimes be confusing): While `care`s are part of the global namespace, they're most extensively used by Clay in particular. In Clay, `care`s specify Clay submodules with specific behaviour, and are used both in scries as well as `task`s and `gift`s. For example, a `%x` `care` reads the data of a file, a `%p` `care` reads file permissions, and so forth. To see all of Clay's `care`s and what they do, you can refer to Clay's [Scry Reference](/docs/arvo/clay/scry).
26+
27+Most other vanes also make use of `care`s in their scry endpoints. While such vanes don't have corresponding submodules with strictly defined behaviour like Clay, the `care`s still confer the general nature of the endpoint. The most widely used `care` is `%x`, which implies reading data in a general sense. Gall has special handling of `%x` scries as described in the [Gall agents](#gall-agents) section below, but otherwise `care`s have no special behaviour for non-Clay vanes (though they must still be included if the endpoint specifies it).
28+
29+### What can I scry?
30+
31+There are two places where scry endpoints are defined:
32+
33+#### Vanes
34+
35+Each of Arvo's eight vanes (kernel modules) include a `+scry` arm which defines that vane's scry endpoints. The number of endpoints and extent of data available varies between vanes. For example, Clay has a very extensive set of scry endpoints which provide read access to all files in all desks across all revisions, as well as the ability to build files, perform `mark` conversions, and various other functions. Jael provides access to a great deal of PKI data. On the other hand, Dill has only a couple of endpoints which are mostly useful for debugging, and Iris has none at all (apart from standard memory reporting endpoints you'd not typically use in your applications).
36+
37+To explore what scry endpoints are available for vanes, you can refer to the Scry Reference section of each vane in the [Arvo](/docs/arvo/overview) section of the documents.
38+
39+#### Gall agents
40+
41+Gall has a single scry endpoint of its own to check for the existence of an agent, but otherwise all Gall scries are passed through to one of the agents it manages. The target agent to scry is specified in place of the `desk` as described in the diagram above. Each Gall agent includes a `+on-peek` arm that defines its own scry endpoints. For example, `%graph-store` has a number of scry endpoints to access the data it stores, such as chat messages and the like.
42+
43+Gall agents can expose scry endpoints with any `care`, but most commonly they'll take a `%x` `care`. Gall handles `%x` scries specially - it expects an extra field at the end of the `path` that specifies a `mark`. Gall will attempt to perform a `mark` conversion from the `mark` returned by the scry endpoint to the `mark` specified. Note the trailing `mark` in the `path` will not be passed through to the agent itself.
44+
45+### What is an endpoint?
46+
47+"Endpoint" refers to a specific scry path in a vane or agent. They will sometimes informally be noted in documentation or source comments like `/x/foo/bar/baz` or maybe just `/foo/bar/baz`. The first part of the former example is the `care`, then the rest is the `path` portion as noted in the diagram earlier.
48+
49+If an agent's scry endpoints don't have formal documentation, you may need to refer to the `+on-peek` arm in its source code to determine its endpoints. While there's no exact pattern that's consistent across different agents, the general pattern will be some initial tests and conversions followed by a `?+` rune, whose cases will typically correspond to the endpoints. The `+on-peek` arm always return a `(unit (unit cage))` (a `cage` is a cell of `[mark vase]`), so each endpoint will typically finish by composing such a structure.
50+
51+Here's an example from the `+on-peek` arm of `%graph-store`:
52+
53+```hoon
54+ [%x %keys ~]
55+:- ~ :- ~ :- %graph-update-2
56+!>(`update:store`[now.bowl [%keys ~(key by graphs)]])
57+```
58+
59+The case in the beginning says it takes a `%x` `care` and has a `path` of `/keys`. Additionally, it returns data with a `mark` of `%graph-update-2`. Assuming we wanted the `%graph-update-2` `mark` converted to a `%json` `mark`, our scry would be composed along the lines of:
60+
61+```hoon
62+.^(json %gx /(scot %p our)/graph-store/(scot %da now)/keys/json)
63+```
64+
65+### Web scries
66+
67+The webserver vane Eyre has a system which allows clients like web browsers to perform scries over HTTP. For details, refer to the [Scry section of Eyre's External API Reference](/docs/arvo/eyre/external-api-ref#scry).
68+
69+### Further reading
70+
71+[dotket](/docs/hoon/reference/rune/dot#-dotket) - Documentation of the `.^` rune which performs scries.
72+
73+[Arvo](/docs/arvo/overview) - Each vane has a Scry Reference section with details of their endpoints as well as examples.
74+
75+[Eyre's External API Reference](/docs/arvo/eyre/external-api-ref#scry) - Documentation of Eyre's scry system which allows web clients to perform scries over HTTP.
76+
77+[The Gall Guide](/docs/userspace/gall/tutorial) - The Gall tutorial includes a section about `+on-peek` and writing scry endpoints.
content/docs/arvo/dill/_index.md+16
@@ -5,3 +5,19 @@ sort_by = "weight"
55template = "sections/docs/chapters.html"
66insert_anchor_links = "right"
77+++
8+
9+[Overview](/docs/avro/dill/dill)
10+
11+A brief overview of the Dill vane.
12+
13+[API Reference](/docs/arvo/dill/tasks)
14+
15+The `task`s Dill takes and the `gift`s it returns.
16+
17+[Scry Reference](/docs/arvo/dill/scry)
18+
19+The scry endpoints of Dill.
20+
21+[Data Types](/docs/arvo/dill/data-types)
22+
23+Reference documentation of the data types used by Dill.
content/docs/arvo/dill/data-types.mdnew file+187
@@ -0,0 +1,187 @@
1++++
2+title = "Data Types"
3+weight = 4
4+template = "doc.html"
5++++
6+
7+Here are the data types used by Dill, as defined in `/sys/lull.hoon`.
8+
9+## `$blew`
10+
11+```hoon
12+ +$ blew [p=@ud q=@ud]
13+```
14+
15+Terminal dimension; `p` is columns, `q` is rows. This structure is passed to Dill by the runtime in a [%blew](/docs/arvo/dill/tasks#blew) `task` whenever the dimensions of the terminal changes.
16+
17+## `$belt`
18+
19+```hoon
20++$ belt
21+ $? bolt
22+ $% [%mod mod=?(%ctl %met %hyp) key=bolt]
23+ [%txt p=(list @c)]
24+ [%ctl p=@c]
25+ [%met p=@c]
26+ == ==
27+```
28+
29+Terminal client input.
30+
31+A `$belt` is passed to Dill in a [%belt](/docs/arvo/dill/tasks#belt) `task` by the runtime whenever there is input, such as a user typing in the console. This is only used between the terminal client and Dill, a [$dill-belt](#dill-belt) is used between Dill and Arvo.
32+
33+May either be a [$bolt](#bolt) or one of:
34+
35+- `%mod` - Modifier (Ctrl, Meta or Hyper) plus [key].
36+- `%txt` - A series of characters
37+- `%ctl` - Ctrl+[key], deprecated in favour of `%mod`.
38+- `%met` - Meta+[key], deprecated in favour of `%mod`.
39+
40+## `$bolt`
41+
42+```hoon
43++$ bolt
44+ $@ @c
45+ $% [%aro p=?(%d %l %r %u)]
46+ [%bac ~]
47+ [%del ~]
48+ [%hit r=@ud c=@ud]
49+ [%ret ~]
50+ ==
51+```
52+
53+Simple input.
54+
55+Either a single simple character or one of:
56+
57+- `%aro` - Arrow keys.
58+- `%bac` - Backspace key.
59+- `%del` - Delete key.
60+- `%hit` - Mouse click - `r` is row and `c` is column. Note these are zero-indexed, with `[0 0]` being the _bottom left_ corner.
61+- `%ret` - Return (Enter) key.
62+
63+## `$blit`
64+
65+```hoon
66++$ blit
67+ $% [%bel ~]
68+ [%clr ~]
69+ [%hop p=@ud]
70+ [%klr p=stub]
71+ [%lin p=(list @c)]
72+ [%mor ~]
73+ [%sag p=path q=*]
74+ [%sav p=path q=@]
75+ [%url p=@t]
76+ ==
77+```
78+
79+Terminal client output.
80+
81+A `$blit` is given to the terminal client by Dill in a `%blit` `gift` when it wants to print some text, clear the screen, go _ding_ or what have you.
82+
83+This is only used between Dill and the terminal client, a [$dill-blit](#dill-blit) is used instead between Arvo and Dill.
84+
85+A `$blit` is one of:
86+
87+- `%bel` - Ring the terminal bell.
88+- `%clr` - Clear the screen.
89+- `%hop` - Set cursor position, `p` specifies its position on the prompt line.
90+- `%klr` - Set styled line, the `$stub` specifies the text and style.
91+- `%lin` - Set current line, `p` contains the text.
92+- `%mor` - Newline.
93+- `%sag` - Save to jamfile, typically in `/[pier]/.urb/put/`. `p` is `/[path]/[filename]/[extension]`. For example, `/foo/bar` will save it in `/[pier]/.urb/put/foo.bar`, `/a/b/c/foo/bar` will save it in `/[pier]/.urb/put/a/b/c/foo.bar`, and `/foo` will save it in `/[pier]/.urb/put.foo`. `q` is the `noun` to `jam` and save in the file.
94+- `%sav` - Save to file. Same behaviour as `%sag` except `q` is an `atom` rather than a `noun` and therefore doesn't need to be `jam`med. The `atom` is written to disk as if it were the bytestring in the tail of an `$octs`. That is, `%sav`ing the `cord` `'abcdef'`, whose `@ux` value is `0x6665.6463.6261`, results in a unix file whose hex dump renders as `61 62 63 64 65 66`.
95+- `%url` - Activate URL, `p` is the URL.
96+
97+## `$dill-belt`
98+
99+```hoon
100++$ dill-belt
101+ $% [%aro p=?(%d %l %r %u)]
102+ [%bac ~]
103+ [%cru p=@tas q=(list tank)]
104+ [%ctl p=@]
105+ [%del ~]
106+ [%hey ~]
107+ [%met p=@]
108+ [%ret ~]
109+ [%rez p=@ud q=@ud]
110+ [%txt p=(list @c)]
111+ [%yow p=gill:gall]
112+ ==
113+```
114+
115+Terminal input for Arvo.
116+
117+While [$belt](#belt) is used between the terminal client and Dill, `$dill-belt` is used between Dill and Arvo.
118+
119+a `$dill-belt` is one of:
120+
121+- `%aro` - Arrow keys.
122+- `%bac` - Backspace key.
123+- `%cru` - Echo error, `p` is an error `@tas` and `q` is a traceback.
124+- `%ctl` - Ctrl+[key].
125+- `%del` - Delete key.
126+- `%hey` - Refresh.
127+- `%met` - Meta+[key].
128+- `%ret` - Return key (Enter).
129+- `%rez` - Terminal resized, `p` is columns and `q` is rows.
130+- `%txt` - Text input.
131+- `%yow` - Connect to app.
132+
133+## `$dill-blit`
134+
135+```hoon
136++$ dill-blit
137+ $% [%bel ~]
138+ [%clr ~]
139+ [%hop p=@ud]
140+ [%klr p=stub]
141+ [%mor p=(list dill-blit)]
142+ [%pom p=stub]
143+ [%pro p=(list @c)]
144+ [%qit ~]
145+ [%out p=(list @c)]
146+ [%sag p=path q=*]
147+ [%sav p=path q=@]
148+ [%url p=@t]
149+ ==
150+```
151+
152+Terminal output from Arvo.
153+
154+While [$blit](#blit) is used between Dill and the terminal client, `$dill-blit` is used between Arvo and Dill.
155+
156+A `$dill-blit` is one of:
157+
158+- `%bel` - Terminal bell.
159+- `%clr` - Clear screen.
160+- `%hop` - Set cursor position, `p` is its horizontal position on the prompt line.
161+- `%klr` - Styled text, the `$stub` specifies both the text and style.
162+- `%mor` - Multiple `$dill-blit`.
163+- `%pom` - Styled prompt, the `$stub` specifies both the text and style.
164+- `%pro` - Set prompt, `p` is the text for the prompt.
165+- `%qit` - Close console.
166+- `%out` - Print text.
167+- `%sag` - Save `noun` to jamfile. See [$blit](#blit) section for further details.
168+- `%sav` - Save `atom` to file. See [$blit](#blit) section for further details.
169+- `%url` - Activate URL.
170+
171+## `$flog`
172+
173+```hoon
174++$ flog
175+ $% [%crop p=@ud]
176+ [%crud p=@tas q=(list tank)]
177+ [%heft ~]
178+ [%meld ~]
179+ [%pack ~]
180+ [%text p=tape]
181+ [%verb ~]
182+ ==
183+```
184+
185+Wrapped Dill `task`s.
186+
187+These are a subset of Dill's `task`s which can be wrapped in a `%flog` `task`. See the [API Reference](/docs/arvo/dill/tasks) document for details of each of these `task`s.
content/docs/arvo/dill/dill-api.mddeleted−366
@@ -1,366 +0,0 @@
1−+++
2−title = "API Reference"
3−weight = 10
4−template = "doc.html"
5−+++
6−
7−In this document we describe the public interface for Dill. Namely, we describe
8−each `task` that Dill can be `pass`ed, and which `gift`(s) Dill can `give` in return.
9−
10−Dill is unique among vanes since it is technically the first vane to be booted (though we
11−regard Jael as the "true" first vane). As a result, there are four `task`s in
12−`task:able:dill` for which Dill acts only as a middleman that passes the `task`s
13−directly to the Arvo kernel. Thus we have split this document into a [Dill
14−tasks](#dill-tasks) section and an [Arvo tasks](#arvo-tasks) section.
15−
16−## Dill Tasks
17−
18−
19−### `%belt`
20−
21−Every keystroke entered into the console triggers a `%belt` `task` for Dill which contains information about the keystroke, such as which key was pressed
22−and from which terminal.
23−
24−#### Accepts
25−
26−`%belt` `task`s come in a number of different types, corresponding to different
27−keystrokes. You can find this list in `+dill-belt` in `zuse.hoon`.
28−
29−```hoon
30−++ dill-belt :: new belt
31− $% {$aro p/?($d $l $r $u)} :: arrow key
32− {$bac ~} :: true backspace
33− {$cru p/@tas q/(list tank)} :: echo error
34− {$ctl p/@} :: control-key
35− {$del ~} :: true delete
36− {$hey ~} :: refresh
37− {$met p/@} :: meta-key
38− {$ret ~} :: return
39− {$rez p/@ud q/@ud} :: resize, cols, rows
40− {$txt p/(list @c)} :: utf32 text
41− {$yow p/gill:gall} :: connect to app
42− == ::
43−```
44−
45−#### Returns
46−
47−Dill returns no `gift` in response to a `%belt` `task`.
48−
49−
50−### `%blew`
51−
52−This `task` is called whenever the terminal emulator is resized, so that Dill can adjust
53−the text output to fit the new window size.
54−
55−#### Accepts
56−
57−```hoon
58−[p=@ud q=@ud]
59−```
60−
61−`p` is the number of columns and `q` is the number of rows.
62−
63−#### Returns
64−
65−Dill returns no `gift` in response to a `%blew` `task`.
66−
67−
68−### `%boot`
69−
70−This `task` is used only once, when Arvo first enters the [adult
71−stage](/docs/arvo/overview#structural-interface-core). Dill is
72−technically the first vane to be activated, via the `%boot` `task`, which then
73−send Jael (considered the the "true" first vane) an `%init` `task`, which then goes on to
74−call `%init` `task`s for other vanes (including Dill).
75−
76−#### Accepts
77−
78−`%boot` takes an argument that is either `%dawn` or `%fake`. `%dawn` is the one
79−used in any typical scenario and activates the ordinary vane initialization
80−process outlined above, while `%fake` is a debugging tool.
81−
82−#### Returns
83−
84−`%boot` does not return a `gift`.
85−
86−
87−
88−### `%crud`
89−
90−This `task` is used by Dill to produce error reports. It prints the received
91−error message(s) (a `(list tank)`) to the terminal. The level of detail of the
92−error message is set by `$log-level`, which is either `%hush`, `%soft`, or `%loud`.
93−
94−#### Accepts
95−
96−```hoon
97−[err=@tas tac=(list tank)]
98−```
99−
100−`err` is the type of error and `tac` is the associated error message.
101−
102−#### Returns
103−
104−`%crud` does not return a `gift`.
105−
106−
107−### `%flog`
108−
109−A `%flog` `task` is a wrapper over a `task` sent by another vane. Dill removes the wrapper
110−and sends the bare `task` to itself over the default `duct`.
111−
112−#### Accepts
113−
114−`%flog` `task`s can take any valid Dill `task` as an argument.
115−
116−#### Returns
117−
118−`%flog` never returns a `gift` on its own, but the wrapped `task` might.
119−
120−#### Example
121−
122−`%crud` `task`s from other vanes are typically passed to Dill wrapped in a
123−`%flog` `task` to print errors to the terminal.
124−
125−
126−
127−### `%flow`
128−
129−This `task` is not used.
130−
131−
132−### `%hail`
133−
134−`%hail` refreshes the terminal by sending an empty message to the default duct
135−for Dill,
136−causing the terminal to redraw itself (?)
137−
138−#### Accepts
139−
140−```hoon
141−[ ~ ]
142−```
143−
144−#### Returns
145−
146−`%hail` returns no `gift`s.
147−
148−
149−### `%heft`
150−
151−`%heft` causes Dill to `pass` a `%wegh` `task` to all other vanes (but not to
152−itself), thus obtaining a complete digest of Arvo's memory usage.
153−
154−#### Accepts
155−
156−```hoon
157−[ ~ ]
158−```
159−
160−#### Returns
161−
162−`%heft` does not directly return any `gift`s, but each `%wegh` `task` sent will
163−return a `%mass` `gift`.
164−
165−
166−### `%hook`
167−
168−This task is not used.
169−
170−
171−### `%harm`
172−
173−This `task` is not used.
174−
175−
176−### `%init`
177−
178−This `task` is called only once, when Arvo first enters the [adult
179−stage](/docs/arvo/overview#structural-interface-core). It performs
180−initial setup for Dill, such as setting the width of the console.
181−
182−Note that this is not actually the first `task` passed to Dill - see [%boot](#%boot).
183−
184−#### Accepts
185−
186−`%init` takes no arguments.
187−
188−#### Returns
189−
190−`%init` returns no `gift`s.
191−
192−
193−### `%knob`
194−
195−`%knob` sets the verbosity level for each error tag.
196−
197−#### Accepts
198−
199−```hoon
200−[tag=@tas level=log-level]
201−```
202−
203−`tag` is the error tag. `level` is the verbosity, set to either `%hush`,
204−`%soft`, or `%loud`.
205−
206−#### Returns
207−
208−`%knob` does not return a gift.
209−
210−
211−
212−
213−### `%noop`
214−
215−`%noop` does nothing - it stands for "no operation".
216−
217−#### Accepts
218−
219−```hoon
220−[ ~ ]
221−```
222−
223−#### Returns
224−
225−This `task`, predictably, returns nothing.
226−
227−#### Example
228−
229−This can be useful if a shell command requires a Dill `task` but you don't want
230−Dill to do anything.
231−
232−
233−
234−### `%talk`
235−
236−This `task` is not used.
237−
238−### `%text`
239−
240−This `task` prints a `tape` to the dojo by first converting it from a UTF8
241−`tape` to a `list` of UTF32 codepoints (`@c`'s) and then recursively popping off a character from the
242−`tape` and sending it to to Unix to be printed into the terminal via a `%blit` event.
243−
244−#### Accepts
245−
246−```hoon
247−[p=tape]
248−```
249−
250−#### Returns
251−
252−This `task` returns zero or more `%blit` `gift`s, each containing a .
253−
254−
255−### `%trim`
256−
257−`%trim` is a common vane `task` used to reduce memory usage. However, it does
258−nothing for Dill.
259−
260−#### Accepts
261−
262−```hoon
263−[p=@ud]
264−```
265−
266−This is a common parameter for `%trim` `task`s across all vanes but does nothing
267−for Dill.
268−
269−#### Returns
270−
271−This `task` returns no `gift`s.
272−
273−
274−
275−### `%vega`
276−
277−This is a common vane `task` used to inform the vane that the kernel has been
278−upgraded. Dill does not do anything in response to this.
279−
280−#### Accepts
281−
282−```hoon
283−[ ~ ]
284−```
285−
286−#### Returns
287−
288−This `task` returns no `gift`s.
289−
290−
291−
292−## Arvo tasks
293−
294−These `task`s live in `task:able:dill` but are passed directly to the Arvo
295−kernel for processing.
296−
297−### `%lyra`
298−
299−This `task` updates the kernel.
300−
301−#### Accepts
302−
303−```hoon
304−[p=@t q=@t]
305−```
306−
307−`p` is the contents of the new `hoon.hoon` and `q` is the contents of the new `arvo.hoon`.
308−
309−#### Returns
310−
311−This `task` returns no `gift`s.
312−
313−
314−### `%pack`
315−
316−This `task` defragments and deduplicates Arvo's memory. This is ultimately
317−performed by the interpreter so you won't find the code in `arvo.hoon` but
318−rather under `c3__pack` in `urbit/worker/main.c`.
319−
320−#### Accepts
321−
322−```hoon
323−[ ~ ]
324−```
325−
326−#### Returns
327−
328−This `task` returns no `gift`s.
329−
330−
331−### `%veer`
332−
333−This `task` is used to install `zuse` and vanes. It is handled by `+veer` in
334−`arvo.hoon`.
335−
336−#### Accepts
337−
338−```hoon
339−[p=@ta q=path r=@t]
340−```
341−
342−`p` is the vane letter, `q` is the formal `path` to the
343−vane, and `r` is the source code for the vane. The vane letter is `%` followed
344−by the first letter of the vane in lowercase, such as `%d` for Dill. `p=%$` in
345−the case of `zuse`.
346−
347−#### Returns
348−
349−This `task` returns no `gift`s.
350−
351−
352−### `%verb`
353−
354−This `task` toggles verbose mode for all of Arvo, which is located here since
355−Dill is the vane that prints errors. To be precise, `%verb` toggles the laconic
356−bit `lac` in the [Arvo state](/docs/arvo/overview#the-state).
357−
358−#### Accepts
359−
360−```hoon
361−[ ~ ]
362−```
363−
364−#### Returns
365−
366−This `task` returns no `gift`s.
content/docs/arvo/dill/dill.md+35−18
@@ -4,21 +4,38 @@ weight = 1
44template = "doc.html"
55aliases = ["/docs/learn/arvo/dill/"]
66+++
7−Our terminal driver.
8−
9−Unix sends keyboard events to `%dill` from either the console or telnet,
10−and `%dill` produces terminal output. The only app that should directly
11−talk to `%dill` is the terminal app. Command-line apps are run by,
12−receive input from, and produce output to, the `%shell` app, which is
13−controlled by `%terminal`, which talks to `%dill`, which talks to unix.
14−Clay also uses `%dill` directly to print out the filesystem change
15−events, but this is questionable behavior.
16−
17−`%dill` has two main components. First, it controls the terminal on a
18−very basic, event-by-event level. This includes keeping track of things
19−like the dimensions of the terminal, the prompt type (plain text or
20−password), which duct to produce effects on, and so forth. Second, it
21−handles terminal events, keystroke by keystroke. Most characters are
22−simply pushed onto the buffer and blitted to the screen, but some
23−characters, including control-modified keys, arrow keys, etc. require
24−special handling. Most of the readline functionality is in `%dill`.
7+
8+The terminal driver vane.
9+
10+Keyboard events and the like from Unix are received by Dill as [%belt](/docs/arvo/dill/tasks#belt) `task`s, and Dill sends `%blit` `gift`s containing [$blit](/docs/arvo/dill/data-types#blit)s back to the runtime to be displayed in the Unix terminal. The manner of interacting with Dill differs depending on whether you're in userspace or kernelspace, as we'll explore below.
11+
12+## Kernelspace
13+
14+For technical reasons, Dill performs a handful of system tasks related to booting a ship and some memory operations. Aside from those, other Vanes mostly just pass Dill [tasks](/docs/arvo/dill/tasks) to print error messages and the like to the terminal.
15+
16+## Userspace
17+
18+Unlike in kernelspace, userspace applications are unlikely to `%pass` Dill `task`s directly. Instead, Dill looks at things in terms of sessions. A session is a pipeline between a client and a handler, where:
19+
20+- The client is an external input source and output sink; a terminal with with dimensions and so forth.
21+- The handler is an application in Urbit that interprets input, maybe does something with it, maybe produces output to be displayed in the client, etc.
22+
23+There are plans for Dill to support multiple independent sessions (i.e. independent terminal clients displaying and processing different information at the same time), but at this stage Dill only supports a single session. The client for this default session is just the Unix terminal via the Urbit runtime. The handler for the default session is the `%hood` app, whose `drum` module does the heavy lifting.
24+
25+`drum` is the CLI app manager. By default you'll have a couple of CLI applications running—`%dojo` and `%chat-cli`—which you can switch between with `ctrl+x`. You may also have additional CLI apps which you have attached with the `|link` command. It's `drum` that keeps track of which one is active, which one input should be routed to, which one should be displayed, what each prompt should look like, and so forth. Dill itself is oblivious to the distinction between these CLI apps. It only sees the default session with `drum`, so it just passes all input to `drum` and display whatever `drum` gives it.
26+
27+While `drum` talks with Dill in `$dill-belt`s and `$dill-blit`s, it talks to CLI apps with `$sole-action`s and `$sole-event`s, which are defined in the `sole` library. For more information on the `sole` library and the related `shoe` library, and for information on how to build CLI apps, you can refer to the [CLI app tutorial](/docs/hoon/guides/cli-tutorial).
28+
29+To give a basic idea of how keyboard events flow through these systems and produce terminal output, here's a diagram showing the messages in pseudo-Hoon:
30+
31+![Dill userspace diagram](https://media.urbit.org/docs/arvo/dill/dill-userspace.svg)
32+
33+You can use a [move trace](/docs/arvo/tutorials/move-trace) to get a hands-on feel for this data flow.
34+
35+## Sections
36+
37+[API Reference](/docs/arvo/dill/tasks) - The `task`s Dill takes and the `gift`s it returns.
38+
39+[Scry Reference](/docs/arvo/dill/scry) - The scry endpoints of Dill.
40+
41+[Data Types](/docs/arvo/dill/data-types) - Reference documentation of the data types used by Dill.
content/docs/arvo/dill/scry.mdnew file+45
@@ -0,0 +1,45 @@
1++++
2+title = "Scry Reference"
3+weight = 3
4+template = "doc.html"
5++++
6+
7+Here are the scry endpoints of Dill. They take a `%x` `care` and require the `desk` in the path prefix be empty, so the general format is `.^([type] %dx /=//=/[some-path])`.
8+
9+Dill only has a couple of scry endpoints, both of which begin with `/session//`. The empty `//` would be where the target session would be specified, but at this stage Dill can only return a result for the default session, so it's always empty.
10+
11+## /sessions//line
12+
13+A scry with a `care` of `%x` and a `path` of `/sessions//line` returns the current text of the prompt line of the default session. The type returned is a [$blit](/docs/arvo/dill/data-types#blit).
14+
15+#### Example
16+
17+```
18+> .^(blit:dill %dx /=//=/sessions//line)
19+[ %lin
20+ p
21+ ~[
22+ ~-~~
23+ ~-z
24+ ~-o
25+ ~-d
26+ ~-~3a.
27+ ~-d
28+ ~-o
29+ ~-j
30+ ~-o
31+ ...truncated for brevity...
32+ ]
33+]
34+```
35+
36+## /sessions//cursor
37+
38+A scry with a `care` of `%x` and a `path` of `/sessions//cursor` returns the current horizontal position of the cursor in the default session. The type returned is a `@ud`.
39+
40+#### Example
41+
42+```
43+> .^(@ud %dx /=//=/sessions//cursor)
44+44
45+```
content/docs/arvo/dill/tasks.mdnew file+342
@@ -0,0 +1,342 @@
1++++
2+title = "API Reference"
3+weight = 2
4+template = "doc.html"
5++++
6+
7+In this document we describe the public interface for Dill. Namely, we describe each `task` that Dill can be `pass`ed, and which `gift`(s) Dill can `give` in return.
8+
9+## `%belt`
10+
11+```hoon
12+[%belt p=belt]
13+```
14+
15+Terminal input.
16+
17+The runtime sends a `%belt` `task` to Dill whenever there is input in the terminal, such as a keystroke.
18+
19+The [$belt](/docs/arvo/dill/data-types#belt) in `p` contains the input such as which key was pressed.
20+
21+Dill will convert the `$belt` into a [$dill-belt](/docs/arvo/dill/data-types#dill-belt) and `%poke` the session handler (typically `drum`) with it.
22+
23+This `task` would not typically be used from userspace.
24+
25+#### Returns
26+
27+Dill returns no `gift` in response to a `%belt` `task`.
28+
29+## `%blew`
30+
31+```hoon
32+[%blew p=blew]
33+```
34+
35+Terminal resized.
36+
37+The runtime passes Dill a `%blew` `task` whenever the terminal is resized.
38+
39+The [$blew](/docs/arvo/dill/data-types#blew) specifies the new dimensions.
40+
41+Dill will convert the `$blew` into a `%rez` [$dill-belt](/docs/arvo/dill/data-types#dill-belt) and `%poke`s the session handler (typically `drum`) with it.
42+
43+This `task` would not typically be used from userspace.
44+
45+#### Returns
46+
47+Dill returns no `gift` in response to a `%blew` `task`.
48+
49+## `%boot`
50+
51+```hoon
52+[%boot lit=? p=*]
53+```
54+
55+This `task` is used only once, when Arvo first enters the [adult stage](/docs/arvo/overview#structural-interface-core). Dill is technically the first vane to be activated, via the `%boot` `task`, which then sends Jael (considered the "true" first vane) the `%dawn` or `%fake` `task` wrapped in the `%boot` `task`. Jael then goes on to call `%init` `task`s for other vanes (including Dill).
56+
57+`lit` specifies whether to boot in lite mode. `p` is either a [%dawn](/docs/arvo/jael/data-types#dawn) or [%fake](/docs/arvo/jael/tasks#fake) `task:jael`. `%dawn` is for an ordinary boot and `%fake` is for booting a fake ship.
58+
59+This `task` would not be used from userspace.
60+
61+#### Returns
62+
63+Dill returns no `gift` in response to a `%boot` `task`, but it will `%pass` the wrapped `%dawn` or `%fake` `task` to Jael.
64+
65+## `%crop`
66+
67+```hoon
68+[%crop p=@ud]
69+```
70+
71+Trim kernel state.
72+
73+This `task` is the same as the [%trim](#trim) `task`. Like `%trim`, Dill does nothing with it.
74+
75+#### Returns
76+
77+Dill returns no `gift` in response to a `%crop` `task`.
78+
79+## `%crud`
80+
81+```hoon
82+[%crud p=@tas q=(list tank)]
83+```
84+
85+Print error.
86+
87+Dill prints the given error to the terminal. The verbosity for the particular `@tas` error tag specified in `p` is determined by the `level` set by a [%knob](#knob) `task` - either `%hush`, `%soft` or `%loud`. The default is `%loud`, where it will print the full `(list tank)` specified in `q`. See [%knob](#knob) for details of verbosity levels.
88+
89+#### Returns
90+
91+Dill does not return a `gift` in response to a `%crud` `task`.
92+
93+## `%flee`
94+
95+```hoon
96+[%flee session=~]
97+```
98+
99+Unwatch a session to which you've previously subscribed with [%view](#view).
100+
101+The ability to specify a session is not yet implemented in Dill, so `session` is always `~`, the default session.
102+
103+#### Returns
104+
105+Dill does not return a `gift` in response to a `%flee` `task`.
106+
107+## `%flog`
108+
109+```hoon
110+[%flog p=flog]
111+```
112+
113+A `%flog` `task` is a wrapper over a `task` sent by another vane. Dill removes the wrapper and sends the bare `task` to itself over the default `duct`.
114+
115+A `%flog` `task` takes a [$flog](/docs/arvo/dill/data-types#flog) as its argument. A `$flog` is a subset of Dill's `task`s.
116+
117+#### Returns
118+
119+Dill does not return a `gift` in response to a `%flog` `task`.
120+
121+## `%flow`
122+
123+```hoon
124+[%flow p=@tas q=(list gill:gall)]
125+```
126+
127+Terminal config.
128+
129+This `task` is not used.
130+
131+## `%hail`
132+
133+```hoon
134+[%hail ~]
135+```
136+
137+Refresh.
138+
139+Dill converts a `%hail` `task` into a `%hey` [$dill-belt](/docs/arvo/dill/data-types#dill-belt) and `%poke`s the session handler (typically `drum`) with it to handle the refresh.
140+
141+This `task` would not be used from userspace.
142+
143+#### Returns
144+
145+Dill returns no `gift` in response to a `%hail` `task`.
146+
147+## `%heft`
148+
149+```hoon
150+[%heft ~]
151+```
152+
153+Produce memory report.
154+
155+When Dill receives a `%heft` `task` it passes Arvo a `%whey` `waif` to obtain a memory report and prints it to the terminal.
156+
157+#### Returns
158+
159+Dill does not return a `gift` in response to a `%heft` `task`.
160+
161+## `%hook`
162+
163+```hoon
164+[%hook ~]
165+```
166+
167+This terminal hung up.
168+
169+This task is not used.
170+
171+## `%harm`
172+
173+```hoon
174+[%harm ~]
175+```
176+
177+All terminals hung up.
178+
179+This `task` is not used.
180+
181+## `%init`
182+
183+```hoon
184+[%init ~]
185+```
186+
187+This `task` is called only once, when Arvo first enters the [adult stage](/docs/arvo/overview#structural-interface-core). It performs initial setup for Dill, such as setting the width of the console.
188+
189+Note that this is not actually the first `task` passed to Dill - see [%boot](#%boot).
190+
191+This `task` would not be used from userspace.
192+
193+#### Returns
194+
195+Dill does not return a `gift` in response to a `%init` `task`.
196+
197+## `%meld`
198+
199+```
200+[%meld ~]
201+```
202+
203+Deduplicate persistent state.
204+
205+Dill asks the runtime to perform the memory deduplication.
206+
207+#### Returns
208+
209+Dill does not return a `gift` in response to a `%meld` `task`.
210+
211+## `%noop`
212+
213+```hoon
214+[%noop ~]
215+```
216+
217+No operation.
218+
219+A `%noop` `task` does nothing, as the name implies.
220+
221+#### Returns
222+
223+Dill does not return a `gift` in response to a `%noop` `task`.
224+
225+## `%pack`
226+
227+```hoon
228+[%pack ~]
229+```
230+
231+Defragment persistent state.
232+
233+Dill asks the runtime to perform the defragmentation.
234+
235+#### Returns
236+
237+Dill does not return a `gift` in response to a `%meld` `task`.
238+
239+## `%talk`
240+
241+```hoon
242+[%talk p=tank]
243+```
244+
245+This `task` is not used.
246+
247+## `%text`
248+
249+```hoon
250+[%text p=tape]
251+```
252+
253+Print `tape` to terminal.
254+
255+Upon receiving a `%text` `task`, Dill will convert the `tape` given in `p` to a `(list @c)` and give it to the runtime in a `%blit` `gift` including a `%lin` [$blit](/docs/arvo/dill/data-types#blit).
256+
257+#### Returns
258+
259+Dill does not return a `gift` in response to a `%text` `task`, but it does give a `%blit` `gift` to the runtime which looks like:
260+
261+```hoon
262+[%blit p=(list blit)]
263+```
264+
265+## `%view`
266+
267+```hoon
268+[%view session=~]
269+```
270+
271+Watch session.
272+
273+A `%view` `task` subscribes for a copy of all `%blit` `gift`s which Dill `%give`s for the default session. This `task` is used by the `%herm` app so it can convert the `$blit`s to JSON and render them in the web terminal.
274+
275+The ability to specify a session is not yet implemented in Dill, so `session` is always `~`, the default session.
276+
277+#### Returns
278+
279+Dill will `%give` a copy of all `%blit`s for the default session. A `%blit` `gift` is:
280+
281+```hoon
282+[%blit p=(list blit)]
283+```
284+
285+## `%trim`
286+
287+```hoon
288+[%trim p=@ud]
289+```
290+
291+`%trim` is a common vane `task` used to reduce memory usage. It does nothing for Dill because Dill only keeps a minimal necessary state.
292+
293+#### Returns
294+
295+Dill does not return a `gift` in response to a `%trim` `task`.
296+
297+## `%vega`
298+
299+```hoon
300+[%vega ~]
301+```
302+
303+This is a common vane `task` used to inform the vane that the kernel has been
304+upgraded. Dill does not do anything in response to this.
305+
306+This `task` would not be used from userspace.
307+
308+#### Returns
309+
310+Dill returns no `gift` in response to a `%vega` `task`.
311+
312+## `%verb`
313+
314+```hoon
315+[%verb ~]
316+```
317+
318+This `task` toggles verbose mode for all of Arvo, which is located here since
319+Dill is the vane that prints errors. To be precise, `%verb` toggles the laconic
320+bit `lac` in the [Arvo state](/docs/arvo/overview#the-state) by passing a `%verb` `waif` to Arvo.
321+
322+#### Returns
323+
324+Dill does not return a `gift` in response to a `%verb` `task`.
325+
326+## `%knob`
327+
328+```hoon
329+[%knob tag=term level=?(%hush %soft %loud)]
330+```
331+
332+`%knob` sets the verbosity level for each error tag. The error `tag` can be any `@tas`. The given `tag` and `level` will be added to Dill's `veb` which maps tags to levels. Subsequent [%crud](#crud) `task`s will then print with the specified verbosity.
333+
334+The `level`s behave like so:
335+
336+- `%hush` - Completely silent, print nothing.
337+- `%soft` - Just print `crud: %error-tag event failed`, ignore any `tang` given in the `%crud`.
338+- `%loud` - Print the `%soft` message as well as the full `tang` given in the `%crud` `task`.
339+
340+#### Returns
341+
342+Dill does not return a `gift` in response to a `%knob` `task`.
content/docs/arvo/eyre/tasks.md+15−15
@@ -20,7 +20,7 @@ This `task` notifies Eyre of the listening HTTPS and HTTP ports. It is automatic
2020
2121The `insecure` field is the HTTP port and `secure` is the optional HTTPS port.
2222
23−### Returns
23+#### Returns
2424
2525Eyre returns no `gift` in response to a `%live` `task`.
2626
@@ -34,7 +34,7 @@ This `task` either configures HTTPS with a certificate and keypair, or configure
3434
3535The [$http-rule](/docs/arvo/eyre/data-types#http-rule) is either tagged with `%cert` or `%turf`. A `%cert` `http-rule` sets an HTTPS certificate and keypair or removes it if null. A `%turf` `http-rule` either adds or removes a DNS binding depending on whether the `action` is `%put` or `%del`. Note that using `%turf` will automatically cause the system to try and obtain a certificate and keypair via Letsencrypt.
3636
37−### Returns
37+#### Returns
3838
3939Eyre returns no `gift` in response to a `%rule` `task`.
4040
@@ -48,7 +48,7 @@ This `task` is how Eyre receives an inbound HTTP request. It will ordinarily be
4848
4949The `secure` field says whether it's over HTTPS. The `address` is the IP address from which the request originated. The [$request:http](/docs/arvo/eyre/data-types#requesthttp) is the HTTP request itself containing the method, URL, headers, and body.
5050
51−### Returns
51+#### Returns
5252
5353Eyre may `pass` a `%response` `gift` on the appropriate `duct` depending on the contents of the `%request`, state of the connection, and other factors.
5454
@@ -60,7 +60,7 @@ Eyre may `pass` a `%response` `gift` on the appropriate `duct` depending on the
6060
6161This `task` is how Eyre receives an inbound HTTP request over the local loopback port. It behaves the same and takes the same arguments as in the [%request](#request) example except it skips any normally required authentication. Just like for a [%request](#request) `task`, you'd not normally use this manually.
6262
63−### Returns
63+#### Returns
6464
6565Eyre may `pass` a `%response` `gift` on the appropriate `duct` depending on the contents of the `%request`, state of the connection, and other factors.
6666
@@ -74,7 +74,7 @@ This `task` is sent to Eyre by the runtime when an HTTP client closes its previo
7474
7575This `task` takes no arguments.
7676
77−### Returns
77+#### Returns
7878
7979Eyre may `pass` a `%response` `gift` on the appropriate `duct` depending on the state of the connection and other factors.
8080
@@ -98,7 +98,7 @@ The `cage` of the second and subsequent `fact`s must have a `mark` of `%http-res
9898
9999Finally, once you've sent all the `fact`s you want, you can `kick` Eyre's subscription and it will complete the response and close the connection to the HTTP client.
100100
101−### Returns
101+#### Returns
102102
103103Eyre will respond with a `%bound` `gift` which says whether the binding was successful and looks like:
104104
@@ -108,7 +108,7 @@ Eyre will respond with a `%bound` `gift` which says whether the binding was succ
108108
109109The `accepted` field says whether the binding succeeded and the `binding` is the requested binding described above.
110110
111−### Example
111+#### Example
112112
113113See the [Direct HTTP Handling With Gall Agents](/docs/arvo/eyre/examples#direct-http-handling-with-gall-agents) section of the [Examples](/docs/arvo/eyre/examples) document for an example.
114114
@@ -140,11 +140,11 @@ The `?` says whether the HTTP request contained a valid session cookie and the [
140140
141141The `simple-payload:http` returned by the generator is similar to the response described in the [%connect](#connect) section except the HTTP headers and body are all contained in the one response rather than staggered across several.
142142
143−### Returns
143+#### Returns
144144
145145Eyre will return a `%bound` `gift` as described at the end of the [%connect](#connect) section.
146146
147−### Example
147+#### Example
148148
149149See the [Generators](/docs/arvo/eyre/examples#generators) section of the [Examples](/docs/arvo/eyre/examples) document for an example.
150150
@@ -158,7 +158,7 @@ This `task` deletes a URL binding previously set by a `%connect` or `%serve` `ta
158158
159159The [$binding](/docs/arvo/eyre/data-types#binding) is the URL path and domain of the binding you want to delete.
160160
161−### Returns
161+#### Returns
162162
163163Eyre returns no `gift` in response to a `%disconnect` `task`.
164164
@@ -172,7 +172,7 @@ This `task` tells Eyre that the web login code has changed, causing Eyre to thro
172172
173173This `task` takes no arguments.
174174
175−### Returns
175+#### Returns
176176
177177Eyre returns no `gift` in response to a `%code-changed` `task`.
178178
@@ -186,11 +186,11 @@ This `task` tells Eyre to start responding positively to CORS requests for the s
186186
187187The [$origin](/docs/arvo/eyre/data-types#origin) is a CORS origin like `http://foo.example` you want to approve.
188188
189−### Returns
189+#### Returns
190190
191191Eyre returns no `gift` in response to a `%approve-origin` `task`.
192192
193−### Example
193+#### Example
194194
195195See the [Managing CORS Origins](/docs/arvo/eyre/examples#managing-cors-origins) section of the [Examples](/docs/arvo/eyre/examples) document for an example.
196196
@@ -204,10 +204,10 @@ This `task` tells Eyre to start responding negatively to CORS requests for the s
204204
205205The [$origin](/docs/arvo/eyre/data-types#origin) is a CORS origin like `http://foo.example` you want want to reject.
206206
207−### Returns
207+#### Returns
208208
209209Eyre returns no `gift` in response to a `%reject-origin` `task`.
210210
211−### Example
211+#### Example
212212
213213See the [Managing CORS Origins](/docs/arvo/eyre/examples#managing-cors-origins) section of the [Examples](/docs/arvo/eyre/examples) document for an example.
content/docs/arvo/iris/tasks.md+3−3
@@ -18,7 +18,7 @@ The [$request:http](/docs/arvo/eyre/data-types#requesthttp) is the request itsel
1818
1919The [$outbound-config](/docs/arvo/iris/data-types#outbound-config) specifies the number of redirects to follow before failing and the number of retries to attempt before giving up. The default values are `5` and `3` respectively. As of writing, **retries and auto-following redirects are not implemented**, so what you specify here is irrelevant and you can just use the bunt value of `outbound-config`.
2020
21−### Returns
21+#### Returns
2222
2323Iris returns a `%http-response` `gift` in response to a `%request` task. A `%response` `gift` looks like:
2424
@@ -32,7 +32,7 @@ The `client-response` structure specifies three kinds of responses - `%progress`
3232
3333Note that neither `%progress` partial messages nor `%cancel` responses have been implemented in Vere at the time of writing, so **you will only ever receive a single `%http-response` `gift` with a `%finished` `client-response`**. If the request fails for some reason, you'll still get an empty `%finished` `client-response` with a `504` status code.
3434
35−### Example
35+#### Example
3636
3737See the [Example](/docs/arvo/iris/example) document.
3838
@@ -46,7 +46,7 @@ Cancel a previous request to fetch a remote HTTP resource.
4646
4747A `%cancel-request` `task` does not take any arguments, the [request](#request) to cancel is determined implicitly.
4848
49−### Returns
49+#### Returns
5050
5151Iris does not return any `gift` in response to a `%cancel-request` `task`. You will also not receive any `gift` back from the original `%request` `task` you've cancelled.
5252
content/docs/arvo/jael/scry.md+15−15
@@ -10,7 +10,7 @@ Jael's scry endpoints never take a `care`. The particular endpoints are specifie
1010
1111A `%step` scry gets the current web login code revision number. It takes your ship name as its `path` and the type returned is a `@ud`.
1212
13−### Example
13+#### Example
1414
1515```
1616> .^(@ud %j /=step=/(scot %p our))
@@ -21,7 +21,7 @@ A `%step` scry gets the current web login code revision number. It takes your sh
2121
2222A `%code` scry gets the current web login code. It takes your ship name as its `path` and the type returned is a `@p`.
2323
24−### Example
24+#### Example
2525
2626```
2727> .^(@p %j /=code=/(scot %p our))
@@ -32,7 +32,7 @@ A `%code` scry gets the current web login code. It takes your ship name as its `
3232
3333A `%life` scry gets the current `life` (key revision number) of a ship if known, otherwise it crashes. It takes a ship as its `path` and the type returned is a `@ud`.
3434
35−### Example
35+#### Example
3636
3737```
3838> .^(@ud %j /=life=/(scot %p our))
@@ -43,7 +43,7 @@ A `%life` scry gets the current `life` (key revision number) of a ship if known,
4343
4444A `%lyfe` scry gets the current unitized `life` (key revision number) of a ship if known, otherwise returns `~`. It takes a ship as its `path` and the type returned is a `(unit @ud)`.
4545
46−### Example
46+#### Example
4747
4848```
4949> .^((unit @ud) %j /=lyfe=/(scot %p our))
@@ -54,7 +54,7 @@ A `%lyfe` scry gets the current unitized `life` (key revision number) of a ship
5454
5555A `%rift` scry gets the current `rift` (continuity number) of a ship if known, otherwise it crashes. It takes a ship as its `path` and the type returned is a `@ud`.
5656
57−### Example
57+#### Example
5858
5959```
6060.^(@ud %j /=rift=/(scot %p our))
@@ -65,7 +65,7 @@ A `%rift` scry gets the current `rift` (continuity number) of a ship if known, o
6565
6666A `%ryft` scry gets the current unitized `rift` (continuity number) of a ship if known, otherwise it returns `~`. It takes a ship as its `path` and the type returned is a `(unit @ud)`.
6767
68−### Example
68+#### Example
6969
7070```
7171> .^((unit @ud) %j /=ryft=/(scot %p our))
@@ -76,7 +76,7 @@ A `%ryft` scry gets the current unitized `rift` (continuity number) of a ship if
7676
7777A `%vein` scry gets your ship's private key for the specified `life`. It takes a `life` `@ud` as its `path` and the type returned is a `ring`.
7878
79−### Example
79+#### Example
8080
8181```
8282> .^(@uw %j /=vein=/1)
@@ -87,7 +87,7 @@ A `%vein` scry gets your ship's private key for the specified `life`. It takes a
8787
8888A `%vile` scry gets your `jam`mmed private boot parameters at your ship's current `life`. It takes no additional arguments in its `path`. The type returned is a `@` which is a `jam`med [$seed](/docs/arvo/jael/data-types#seed).
8989
90−### Examples
90+#### Examples
9191
9292```
9393> .^(@ %j /=vile=)
@@ -108,7 +108,7 @@ A `%vile` scry gets your `jam`mmed private boot parameters at your ship's curren
108108
109109A `%deed` scry gets the `life`, pubkey and maybe a signature if the ship in question is a comet. It takes the target ship and life as its `path` like `/~sampel-palnet/1` and the type returned is a `[life pass (unit @ux)]`.
110110
111−### Example
111+#### Example
112112
113113```
114114> .^([life pass (unit @ux)] %j /=deed=/(scot %p our)/1)
@@ -122,7 +122,7 @@ A `%deed` scry gets the `life`, pubkey and maybe a signature if the ship in ques
122122
123123A `%earl` scry deterministically derives a private key for a moon. Note this is a holdover from an old version of moon infrastructure, the current `|moon` generator doesn't use this and instead generates moon private keys non-deterministically. It takes the moon name and your ship's current `life` (not the moon's `life`) as its `path` like `/~doznec-dozzod-dozzod-dozzod/1`. The type returned is a [$seed](/docs/arvo/jael/data-types#seed), the `life` of the moon in the `lyf` field of the `seed` will always be `1`.
124124
125−### Example
125+#### Example
126126
127127```
128128> .^(seed:jael %j /=earl=/~doznec-dozzod-dozzod-dozzod/1)
@@ -138,7 +138,7 @@ A `%earl` scry deterministically derives a private key for a moon. Note this is
138138
139139A `%sein` scry gets the sponsor for the specified ship. This scry is used implicitly by `sein:title`. It takes the target ship as its `path` and the type returned is a `@p`.
140140
141−### Example
141+#### Example
142142
143143```
144144> .^(@p %j /=sein=/~sampel-palnet)
@@ -149,7 +149,7 @@ A `%sein` scry gets the sponsor for the specified ship. This scry is used implic
149149
150150A `%saxo` scry gets the sponsorship chain for the target ship (including the target ship itself). For example, a `%saxo` scry for a planet will return the planet, its star and the star's galaxy. This scry is used implicitly by `saxo:title`. It takes the target ship as its `path` and the type returned is a `(list @p)`.
151151
152−### Example
152+#### Example
153153
154154```
155155> .^((list @p) %j /=saxo=/~sampel-palnet)
@@ -168,7 +168,7 @@ nel=(set duct) :: trackers of all
168168
169169The `yen` `jug` maps subscribed `duct`s to the `ship`s they're tracking, and `ney` is just the inverse, mapping tracked `ship`s to subscribed `duct`s. The `nel` set contains `duct`s track all public key updates.
170170
171−### Example
171+#### Example
172172
173173```
174174.^([yen=(jug duct ship) ney=(jug ship duct) nel=(set duct)] %j /=subscriptions=/1)
@@ -179,7 +179,7 @@ The `yen` `jug` maps subscribed `duct`s to the `ship`s they're tracking, and `ne
179179
180180A `%sources` scry gets the current state of Jael's sources for PKI updates. It takes no additional arguments in its `path` and returns a [$state-eth-node](/docs/arvo/jael/data-types#state-eth-node).
181181
182−### Example
182+#### Example
183183
184184```
185185> .^(state-eth-node:jael %j /=sources=)
@@ -196,7 +196,7 @@ A `%sources` scry gets the current state of Jael's sources for PKI updates. It t
196196
197197A `%turf` scry gets the list of domain prefixes for galaxies. It takes no additional arguments in its `path`. It returns a `(list turf)`, where a `turf` is a TLD-first `(list @t)`, so `urbit.org` as a `turf` is `~['org' 'urbit']`.
198198
199−### Example
199+#### Example
200200
201201```
202202> .^((list turf) %j /=turf=)
content/docs/arvo/jael/tasks.md+15−15
@@ -34,7 +34,7 @@ This `task` takes a [$dawn-event](/docs/arvo/jael/data-types#dawn-event) as its
3434
3535You would not use this `task` manually.
3636
37−### Returns
37+#### Returns
3838
3939Jael `%give`s an `%init` `gift` to Unix. This occurs after the Dill `%slip` init.
4040
@@ -54,7 +54,7 @@ The `ship` field specifies the `@p` of the fake ship being created.
5454
5555You would not use this `task` manually.
5656
57−### Returns
57+#### Returns
5858
5959Jael `%give`s a `%init` `gift` to Unix.
6060
@@ -72,7 +72,7 @@ Sets the source that the public keys for a set of `ship`s should be obtained fro
7272
7373You are unlikely to use this `task` manually.
7474
75−### Returns
75+#### Returns
7676
7777Jael will not return any `gift`s in response to a `%listen` `task`.
7878
@@ -94,7 +94,7 @@ Register moon keys or otherwise administer a moon.
9494
9595This is what is sent to Jael by `%hood` behind the scenes when you run `|moon`, `|moon-breach` or `|moon-cycle-keys`. The `ship` field is the moon's `@p`. The [$udiff:point](/docs/arvo/jael/data-types#udiffpoint) will contain the bunt of an [$id:block](/docs/arvo/jael/data-types#idblock) (since moons aren't registered in Azimuth) and one of the `udiff` actions depending on what you want to do.
9696
97−### Returns
97+#### Returns
9898
9999Jael does not return any `gift`s in response to a `%moon` `task`.
100100
@@ -110,11 +110,11 @@ If you've subscribed to public or private key updates from Jael with a [%private
110110
111111To cancel a subscription to the ship's private keys you must leave `whos` empty like `[%nuke ~]`.
112112
113−### Returns
113+#### Returns
114114
115115Jael does not return a `gift` in response to a `%nuke` `task`.
116116
117−### Examples
117+#### Examples
118118
119119See the [%public-keys and %nuke](/docs/arvo/jael/examples#public-keys-and-nuke) section of the Examples document for an example of using `%nuke` to cancel a `%public-keys` subscription. See the thread in the [%private-keys](/docs/arvo/jael/examples#private-keys) example for cancelling a `%private-keys` subscription.
120120
@@ -128,7 +128,7 @@ Subscribe to private key updates.
128128
129129Subscribe to be notified of private key updates for the local ship. The subscription will continue until Jael receives a [%nuke](#nuke) `task` to cancel it.
130130
131−### Returns
131+#### Returns
132132
133133Jael responds to a `%private-keys` `task` with a `%private-keys` `gift` which looks like:
134134
@@ -138,7 +138,7 @@ Jael responds to a `%private-keys` `task` with a `%private-keys` `gift` which lo
138138
139139The `life` is the current life of the ship and the `vein` `map` contains the private key for each life up to (and including) the current one. Upon first subscribing, Jael will immediately respond with a `%private-keys` `gift`. Then, whenever the ship's private keys are changed, it'll send a new and updated `%private-keys` `gift`.
140140
141−### Example
141+#### Example
142142
143143See the [%private-keys](/docs/arvo/jael/examples#private-keys) section of the Examples document for a practical example.
144144
@@ -152,7 +152,7 @@ Subscribe to public key (and related) updates from Jael.
152152
153153An agent or thread can subscribe to be notified of public key updates, sponsorship changes and continuity breaches for the `set` of `ship`s specified in the `ships` field. The subscription will continue until Jael receives a [%nuke](#nuke) `task` to cancel it.
154154
155−### Returns
155+#### Returns
156156
157157Jael responds to a `%public-keys` `task` with `%public-keys` `gift`s which look like:
158158
@@ -164,7 +164,7 @@ The [$public-keys-result](/docs/arvo/jael/data-types#public-keys-result) contain
164164
165165Upon subscription, Jael will immeditely respond with a `%public-keys` `gift` containing a `%full` `public-keys-result` with the public key for each `life` up until the current one for each `ship` specified in the original `task`. After than, Jael will send a `%public-keys` `gift` with either a `%diff` or `%breach` `public-keys-result` each time a change occurs for any of the `ship`s to which you're subscribed.
166166
167−### Example
167+#### Example
168168
169169See the [%public-keys and %nuke](/docs/arvo/jael/examples#public-keys-and-nuke) section of the Examples document for a practical example.
170170
@@ -178,7 +178,7 @@ Update private keys.
178178
179179This is what is sent to Jael by `%hood` when you run `|rekey`, as you must after setting new Azimuth keys or running `|cycle-moon-keys` on a moon's parent. It will update your `life` (key revision number) and private keys. The `life` field is the new `life` (typically an increment of the current `life`) and the `ring` is a private key `@`.
180180
181−### Returns
181+#### Returns
182182
183183Jael does not return any `gift` in response to a `%rekey` `task`.
184184
@@ -192,7 +192,7 @@ View domains.
192192
193193The domains returned by a `%turf` `task` are used as the base for individual galaxy domain names (e.g. from `urbit.org` you get `zod.urbit.org`, `bus.urbit.org`, etc). Jael gets these from Azimuth, then Ames gets them from Jael and passes them to the runtime, which will perform the DNS lookups and give Ames back the galaxy IP addresses. A `%turf` task takes no additional arguments. You're unlikely to use this manually - if you want the current `turf`s you'd likely want to do a [turf scry](/docs/arvo/jael/scry#turf) instead.
194194
195−### Returns
195+#### Returns
196196
197197Jael will respond to a `%turf` `task` with a `%turf` `gift`, which looks like:
198198
@@ -202,7 +202,7 @@ Jael will respond to a `%turf` `task` with a `%turf` `gift`, which looks like:
202202
203203The `turf` in the `(list turf)` is a domain as a `(list @t)`, TLD-first. The current default is `[['org' 'urbit' ~] ~]`.
204204
205−### Example
205+#### Example
206206
207207See the [%turf section of the Examples document](/docs/arvo/jael/examples#turf) for a practical example.
208208
@@ -216,10 +216,10 @@ Reset web login code.
216216
217217Jael maintains a `step` value that represents the web login code revision number, and uses it to derive the code itself. It begins at `0` and is incremented each time the code is changed. When Jael updates the web login code, it sends Eyre a `%code-changed` `task:eyre` so that Eyre can throw away all of its current cookies and sessions. A `%step` task takes no additional argument.
218218
219−### Returns
219+#### Returns
220220
221221Jael does not return a `gift` in response to a `%step` `task`.
222222
223−### Example
223+#### Example
224224
225225See the [%step](/docs/arvo/jael/examples#step) section of the Examples document for a practical example.
content/docs/hoon/reference/cheat-sheet.md+3−13
@@ -5,24 +5,14 @@ template = "doc.html"
55aliases = ["docs/reference/cheat-sheet/"]
66+++
77
8−Download the cheat sheet PDF [here](https://storage.googleapis.com/media.urbit.org/docs/hoon-cheat-sheet-2020-07-24.pdf).
8+Download the cheat sheet PDF [here](https://media.urbit.org/docs/hooncard-unbranded-2021-09-12.pdf)
99
1010We've found it's useful to have a quick reference of Hoon's syntax always at
11−hand. So above is a download link to a Hoon cheatsheet .pdf. It's missing a
12−few of the literal syntaxes, but it covers the most common ones.
11+hand. So above is a download link to a Hoon cheatsheet PDF.
1312
14−This doesn't list any of the Ford runes, although there is now at least
15−some documentation for those [here](/docs/arvo/ford/ford).
16−
17−The following runes also exist, but are currently undocumented and not listed in
13+The following rune also exists, but is currently undocumented and not listed in
1814the cheat sheet:
1915
2016```
21−!: :: turn on debugging printfs
22−!. :: turn off debugging printfs
23−!; :: using the "type of type", emit the type for an expression
24−!, :: emit AST of expression
2517$* :: bunt (irregular form is *)
26−^. :: use gate to transform type
27−^& :: zinc (covariant) -- see the docs on advanced types
2818```
content/docs/hoon/reference/rune/zap.md+1−1
@@ -196,7 +196,7 @@ The syntax difference is that a test for equality takes two subexpressions, and
196196##### Produces
197197
198198`q`, if `p` is greater than or equal to the Hoon kelvin version.
199−(Versions count down; the current version is 141.)
199+(Versions count down; the current version is 140.)
200200
201201##### Syntax
202202
content/docs/hoon/reference/style.md+108−108
@@ -79,32 +79,32 @@ across the page.
7979With one child:
8080
8181```hoon
82− !:
83− a
82+!:
83+a
8484```
8585
8686With two:
8787
8888```hoon
89− =+ a
90− b
89+=+ a
90+b
9191```
9292
9393With three:
9494
9595```hoon
96− ?: a
97− b
98− c
96+?: a
97+ b
98+c
9999```
100100
101101With four (the maximum rune fanout):
102102
103103```hoon
104− :^ a
105− b
106− c
107− d
104+:^ a
105+ b
106+ c
107+d
108108```
109109
110110It is sometimes acceptable to not backstep -- especially in tuple
@@ -118,21 +118,21 @@ they can’t self-terminate. They are terminated by a `==` marker.
118118The sequence, or its variable part, is indented and vertical:
119119
120120```hoon
121− :* a
122− b
123− c
124− ==
121+:* a
122+ b
123+ c
124+==
125125```
126126
127127Another representation wastes a line, but saves an indent. Use
128128this only for very long sequences:
129129
130130```hoon
131− :*
132− a
133− b
134− c
135− ==
131+:*
132+ a
133+ b
134+ c
135+==
136136```
137137
138138Some variable sequences are sequences of pairs; some of these
@@ -143,29 +143,29 @@ looks better for this particular code.
143143Kingside format:
144144
145145```hoon
146− ?+ x default
147− %foo 99
148− %foobar ?: y
149− 42
150− (add 2 2)
151− 37 36
152− ==
146+?+ x default
147+ %foo 99
148+ %foobar ?: y
149+ 42
150+ (add 2 2)
151+ 37 36
152+==
153153```
154154
155155Queenside format:
156156
157157```hoon
158− ?+ x
159− default
160− ::
161− %foo
162− 99
163− ::
164− %foobar
165− ?: y
166− 42
167− (add 2 2)
168− ==
158+?+ x
159+ default
160+::
161+ %foo
162+ 99
163+::
164+ %foobar
165+ ?: y
166+ 42
167+ (add 2 2)
168+==
169169```
170170
171171The queenside format is more useful when the tails are bigger.
@@ -176,13 +176,13 @@ We sometimes end up with multiple terminators on separate lines,
176176two or more spaces apart. These lines can be collapsed:
177177
178178```hoon
179− ?+ x default
180− %foo 99
181− 37 36
182− %foobar ?- y
183− %moo 9
184− %bar 10
185− == ==
179+?+ x default
180+ %foo 99
181+ 37 36
182+ %foobar ?- y
183+ %moo 9
184+ %bar 10
185+== ==
186186```
187187
188188## General naming style
@@ -220,25 +220,25 @@ Hoon suggests “breathing comments.” There should always be an
220220empty comment line between a comment and the line **below** it:
221221
222222```hoon
223− :: look, code
224− ::
225− this.is.code
226− :: we are going to need more code
227− ::
228− this.is.more.code
223+:: look, code
224+::
225+this.is.code
226+:: we are going to need more code
227+::
228+this.is.more.code
229229```
230230
231231Sometimes dense code does need air on both sides. Also
232232legitimate:
233233
234234```hoon
235− :: look, code
236− ::
237− this.is.code
238− ::
239− :: we are going to need more code
240− ::
241− this.is.more.code
235+:: look, code
236+::
237+this.is.code
238+::
239+:: we are going to need more code
240+::
241+this.is.more.code
242242```
243243
244244But air on both sides is needed less often than you may think.
@@ -257,15 +257,15 @@ In older code you’ll also see attempts to produce large, visually
257257salient geometries as separators, like:
258258
259259```
260− ::
261− ::::
262− ::
260+::
261+::::
262+::
263263```
264264
265265or
266266
267267```
268− ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
268+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
269269```
270270
271271Please don’t do this in new code. Normal conventions should be
@@ -300,14 +300,14 @@ then an udon body with paragraphs broken by empty lines, indented
300300four spaces, in ASCII mixed case:
301301
302302```hoon
303− :: $foo: the definition of a foo
304− ::
305− :: The source of the word “foo” is lost in history. Some
306− :: think it comes from the WWII military term “FUBAR,”
307− :: in which “UBAR” stands for “up beyond all retrieval.”
308− ::
309− :: Somehow “FUBAR” became “foobar.” The rest is history.
310− ::
303+:: $foo: the definition of a foo
304+::
305+:: The source of the word “foo” is lost in history. Some
306+:: think it comes from the WWII military term “FUBAR,”
307+:: in which “UBAR” stands for “up beyond all retrieval.”
308+::
309+:: Somehow “FUBAR” became “foobar.” The rest is history.
310+::
311311```
312312
313313The point is: sometimes we want to see a deep explanation;
@@ -321,9 +321,9 @@ A feature headline is `::`, then two spaces, then a qualified
321321location, then `:`, then a freeform string. Like:
322322
323323```hoon
324− :: $foo: a the definition of a foo
325− ::
326− +$ foo [head=@ tail=^]
324+:: $foo: a the definition of a foo
325+::
326++$ foo [head=@ tail=^]
327327```
328328
329329We find the feature `$foo` relative to the current location and
@@ -337,9 +337,9 @@ Product comments describe the product of the expression below.
337337The headline is a string in parentheses:
338338
339339```hoon
340− :: (a very strange $foo indeed)
341− ::
342− make:a:strange:foo
340+:: (a very strange $foo indeed)
341+::
342+make:a:strange:foo
343343```
344344
345345### Flow comments and legends
@@ -364,15 +364,15 @@ make it … `[%wtcl if=hoon then=hoon else=hoon]`. Then we
364364could write flow comments as follows:
365365
366366```hoon
367− ?: :: if, it is winter
368− ::
369− is-winter
370− :: then, why not rome?
371− ::
372− visit-italy
373− :: else, amsterdam is nice in the summer
367+?: :: if, it is winter
374368 ::
375− visit-holland
369+ is-winter
370+ :: then, why not rome?
371+ ::
372+ visit-italy
373+:: else, amsterdam is nice in the summer
374+::
375+visit-holland
376376```
377377
378378The headline of a flow comment uses a **legend**: the name of
@@ -392,35 +392,35 @@ Sometimes, just for cosmetic reasons, we like to raise the
392392comment on the first leg of a rune up above the parent:
393393
394394```hoon
395− :: if, it is winter
396− ::
397− ?: is-winter
398− :: then, why not rome?
399− ::
400− visit-italy
401− :: else, amsterdam is nice in the summer
402− ::
403− visit-holland
395+:: if, it is winter
396+::
397+?: is-winter
398+ :: then, why not rome?
399+ ::
400+ visit-italy
401+:: else, amsterdam is nice in the summer
402+::
403+visit-holland
404404```
405405
406406This can make flow comments collide, as in “else, if,” below:
407407
408408```hoon
409− :: if, it is winter
410− ::
411− ?: is-winter
412− :: then, why not rome?
413− ::
414− visit-italy
415− :: else, if, sprechen sie deutsch?
416− ::
417− ?: speak-german
418− :: then, maybe berlin?
419− ::
420− visit-berlin
421− :: else, everyone in amsterdam speaks english
422− ::
423− visit-holland
409+:: if, it is winter
410+::
411+?: is-winter
412+ :: then, why not rome?
413+ ::
414+ visit-italy
415+:: else, if, sprechen sie deutsch?
416+::
417+?: speak-german
418+ :: then, maybe berlin?
419+ ::
420+ visit-berlin
421+:: else, everyone in amsterdam speaks english
422+::
423+visit-holland
424424```
425425
426426#### Flow comments, traces, and interpolation
content/docs/userspace/graph-store/sample-application-overview.mdnew file+369
@@ -0,0 +1,369 @@
1++++
2+title = "Sample Application: Library"
3+weight = 4
4+sort_by = "weight"
5+template = "doc.html"
6+insert_anchor_links = "right"
7++++
8+
9+
10+
11+Library is a sample social media application in which you can create a collection of books, called a library, which can contain any number of books.
12+You can share individual collections per-ship. The creator of the collection has de-facto admin powers; he is the only one who can add or remove books to/from the library,
13+remove the library itself, and add comments or remove anyone's comments. Guest ships may request access to specific libraries, which if granted,
14+allows them to request any book from the library.
15+
16+Using this application, you can:
17+
18+- Create libraries, where a library is a collection of books
19+- Add and remove books from a library, if you are the owner
20+- Allow others to view your library based on various permissioning schemes (policies)
21+- Add and remove comments from a library, if you are the owner or were granted access
22+
23+## Prerequisites
24+
25+This overview is a companion document to the *Library* application, and assumes you are familiar with `%graph-store`. Before continuing, **you must familiarize yourself with the sample application**, which can be found [here](https://github.com/ynx0/library). See the [detailed usage guide](https://github.com/ynx0/library/blob/master/README.md) for download and installation instructions.
26+
27+
28+## Project Structure
29+
30+Here is the directory structure of our app.
31+```
32+├── app
33+│ └── library-proxy.hoon
34+├── lib
35+│ └── library.hoon
36+├── mar
37+│ ├── graph
38+│ │ └── validator
39+│ │ └── library.hoon
40+│ └── library
41+│ ├── action.hoon
42+│ ├── command.hoon
43+│ └── response.hoon
44+├── sur
45+│ └── library.hoon
46+└── install.sh
47+```
48+
49+- The main code of the application lives in [`app/library-proxy.hoon`](https://github.com/ynx0/library/blob/master/app/library-proxy.hoon). This contains the gall agent which proxies the `%graph-store` updates between ships.
50+
51+- [`lib/library.hoon`](https://github.com/ynx0/library/blob/master/lib/library.hoon) contains miscellaneous helper arms which the proxy uses extensively. It mainly contains arms that construct different `%graph-store` updates various actions that a user performed.
52+
53+- The `mar/library` folder contains the definitions of the pokes that `%library-proxy` uses.
54+
55+- Contained in [`mar/graph/validator/library.hoon`](https://github.com/ynx0/library/blob/master/mar/graph/validator/library.hoon) is the definition of the validation logic that `%graph-store` uses to enforce the schema of the library applications graph data.
56+
57+- [`sur/library.hoon`](https://github.com/ynx0/library/blob/master/sur/library.hoon) contains all the various type definitions used by `%library-store`.
58+
59+- [`install.sh`](https://github.com/ynx0/library/blob/master/install.sh) is a script that automates copying the source files into a ship's pier / home desk.
60+
61+
62+## Implementation Summary
63+
64+The project contains a custom `%graph-store` validator tht specifies the application's schema and a custom gall agent, called `%library-proxy`, which sends `%graph-store` updates between the host ship and subscribers.
65+The gall agent also generates `%graph-store` updates based on commands and actions that a user issues. Commands can only be issued to a proxy by the ship owner,
66+while actions can be issued by any ship, but may fail based on permissions. A host ship's `%library-proxy` talks to the subscriber ship's `%library-proxy` through the normal gall app channels (pokes/peeks)
67+to send them any updates that have taken place on the library graph. It is the host's responsibility to forward all relevant updates to subscribers
68+(kept track of in a data structure defined [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/sur/library.hoon#L14-L15)),
69+while subscribers must only trust graph updates that it receives from the owner of that resource.
70+
71+Here are the proxy's responsibilities:
72+
73+- **Application-specific API** - presents an interface for a user to interact with both his/her own library proxy and others' by directly defining and implementing a user-facing API as pokes and scries,
74+ as opposed to forcing the user to deal with the graph-store API directly. The `command`/`action`/`response` poke types are defined [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/sur/library.hoon#L17-L42),
75+ while the scries are defined [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L132).
76+- **Graph store update creation** - creates the appropriate graph update for a given user-facing action
77+- **Graph store update proxying** - handles the networking and subscription logic required to send graph store updates between host and subscriber
78+- **Access control** - allows or denies ships access to libraries, checks for proper permissions before processing a `command` or `action`
79+
80+## Interaction with `%graph-store`
81+
82+There are a couple of ways that our application uses and interacts with `%graph-store`.
83+Firstly, the app subscribes to `%graph-store` in `+on-init` on path `/updates`, meaning it will be notified of every single `graph-update` that occurs on the local ship. (*Recall the type of `graph-update`, defined [here](https://github.com/urbit/urbit/blob/ceed4b78d068d7cb70350b3cd04e7525df1c7e2d/pkg/arvo/sur/graph-store.hoon#L29), the reference for which can be found [here](https://urbit.org/docs/userspace/graph-store/data-structure-overview/#update-part-1)*). We also define a validator for our application that `%graph-store` uses for every *Library* graph. Since `%graph-store` registers all validators under the path `%/graph/validator/<name of app>/hoon`, we don't have to do anything special to register our validator. We just need to correctly name our file, and provide an `+graph-indexed-post` arm which should perform all schema validation.
84+
85+### Synchronizing Graphs Between Ships
86+
87+An owner is responsible for forwarding any updates to clients. Whenever the `%library-proxy` gall agent receives an update from it's local `%graph-store`, it checks to see whether it's for a graph it owns or not. If it's not, we skip sending out updates since we don't own the resource\*. If it is, then we generate cards to poke each subscriber with that same `graph-update`. This logic occurs within the agent's `+on-watch` arm, with the logic residing in the `handle-outgoing-graph-update` arm, found [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L345).
88+
89+On the receiving end, since we know that we are not the source of the `graph-update`, we handle the update in the `handle-incoming-graph-store` helper arm, which makes sure to only process and forward graph store updates to local graph store that are sent by the owner, and no one else. Once it passes this permissions check, it is poked into the local graph like any other graph-update.
90+
91+\**One could imagine a gossip style protocol where the data is like a fact so it doesn't matter where it came from, but in this app the library host is defined as the source of truth.*
92+
93+## Data Model
94+
95+There are two main application-side data structures that we define.
96+The first type is [`book`](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/sur/library.hoon#L4-L9) which contains a title and an isbn.
97+The second type is a [`comment`](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/sur/library.hoon#L3), which is just a simple string of text,
98+meant to represent a comment on any given book.
99+
100+### Representing Data using `%graph-store`
101+
102+The data types we defined for our application do not fit within `%graph-store` out of the box. `%graph-store` doesn't allow arbitrarily typed data in a node's content field, so we create an ad-hoc representation that we can cleanly convert to and from our own data types and `%graph-store` types.
103+
104+Here is the conversion code: [(link)](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/lib/library.hoon#L12-L19)
105+
106+```
107+++ make-meta-contents
108+ |= [=book:library]
109+ ^- (list content)
110+ ~[[%text title.book] [%text isbn.book]]
111+++ make-comment-contents
112+ |= [comment-text=comment:library]
113+ ^- (list content)
114+ ~[[%text comment-text]]
115+```
116+
117+Each arm takes in either a `book` or a `comment`, our application specific data type, and spits out a `(list content)`, which is the type that a `node` uses to store its content.
118+
119+### Organizing Data within `%graph-store`
120+
121+We define 3 core structures:
122+
123+- **Library** - The fundamental data structure. A library is a graph that contains books
124+- **Book** - A data structure that contains an entry of a given book's metadata (i.e. title and isbn) and any comments associated with it. It is represented by a top-level node within a library graph
125+- **Comment** - A node that represents a user's comment on a given book. Represented as a child node of a book's comment container node.
126+
127+We then define the layout of the data as follows:
128+
129+* Library
130+ - Book
131+ + Metadata Revision Container
132+ * Specific Metadata Revision
133+ + Comments Container
134+ * Specific Comment
135+
136+Every graph created with the `%graph-validator-library` is a *Library* graph, and thus it has the following characteristics:
137+
138+- Every Library graph represents a library, or a collection of books
139+- Every top level node in a library represents a book and all related contents
140+- Within a book there are always two structural nodes: a container node for metadata revisions, and a container node that holds all comments, modeling a comments section
141+
142+To make things more concrete, let's look at an example of an actual graph.
143+
144+(*For the sake of clarity, we have replaced each index with its representation in its original aura in following output.*)
145+
146+```
147+[ p=[entity=~zod name=%library1]
148+ q
149+ [ p
150+ { [ key=~2021.6.27..14.18.57..b3dc
151+ val
152+ [ post
153+ [ %.y
154+ p
155+ [ author=~zod
156+ index=~[~2021.6.27..14.18.57..b3dc]
157+ time-sent=~2021.6.27..14.18.57..b3dc
158+ contents=~
159+ hash=~
160+ signatures={}
161+ ]
162+ ]
163+ children
164+ [ %graph
165+ p
166+ { [ key=%comments
167+ val
168+ [ post
169+ [ %.y
170+ p
171+ [ author=~zod
172+ index=~[~2021.6.27..14.18.57..b3dc %comments]
173+ time-sent=~2021.6.27..14.18.57..b3dc
174+ contents=~
175+ hash=~
176+ signatures={}
177+ ]
178+ ]
179+ children
180+ [ %graph
181+ p
182+ { [ key=~2021.6.27..14.20.00.b5fc
183+ val
184+ [ post
185+ [ %.y
186+ p
187+ [ author=~pub
188+ index=~[~2021.6.27..14.18.57..b3dc %meta ~2021.6.27..14.20.00.b5fc]
189+ time-sent=~2021.6.27..14.20.00.b5fc
190+ contents=~[[%text text='dune is pretty good']]
191+ hash=~
192+ signatures={}
193+ ]
194+ ]
195+ children=[%empty ~]
196+ ]
197+ ]
198+ }
199+ ]
200+ ]
201+ ]
202+ [ key=%meta
203+ val
204+ [ post
205+ [ %.y
206+ p
207+ [ author=~zod
208+ index=~[~2021.6.27..14.18.57..b3dc %meta]
209+ time-sent=~2021.6.27..14.18.57..b3dc
210+ contents=~
211+ hash=~
212+ signatures={}
213+ ]
214+ ]
215+ children
216+ [ %graph
217+ p
218+ { [ key=2
219+ val
220+ [ post
221+ [ %.y
222+ p
223+ [ author=~zod
224+ index=~[~2021.6.27..14.18.57..b3dc %meta 1]
225+ time-sent=~2021.6.27..14.18.58..a4ed
226+ contents=~[[%text text='Dune'] [%text text='0441172717']]
227+ hash=~
228+ signatures={}
229+ ]
230+ ]
231+ children=[%empty ~]
232+ ]
233+ ]
234+ [ key=1
235+ val
236+ [ post
237+ [ %.y
238+ p
239+ [ author=~zod
240+ index=~[~2021.6.27..14.18.57..b3dc %meta 1]
241+ time-sent=~2021.6.27..14.18.57..b3dc
242+ contents=~[[%text text='Dune......'] [%text text='0444444444']]
243+ hash=~
244+ signatures={}
245+ ]
246+ ]
247+ children=[%empty ~]
248+ ]
249+ ]
250+ }
251+ ]
252+ ]
253+ ]
254+ }
255+ ]
256+ ]
257+ ]
258+ }
259+ q=[~ %graph-validator-library]
260+ ]
261+]
262+```
263+
264+![](https://lh5.googleusercontent.com/UTCnXumWvuPpFtduxgzmxIR_vhn9N3SylJ9h-MoUjYWiCT4AZo4BXY1mWceu0iHcJjesWoBDzMVO9QYWbnqtsGWa4nt4GlIRAtiC2zSXLkDUEttTmlMDLS7XRZ7ZhJyEayaMMl2r)
265+
266+* The above graph represents a library that has a single book, whose metadata is stored under the `%meta` revision container. The metadata associated with this specific book entry is currently with title: `"Dune"` and isbn `"0441172717"`.
267+ The reason we have a revision container for the book metadata is so that in case someone makes an error, they may correct it. A potential frontend could then simply show the most recent version.
268+* Each comment is simply a node under a book's `%comment` node with a single `%text` content.
269+ We skipped revision containers for comments to keep schema simple, but you could imagine duplicating the same logic for comments as well.
270+* Every book node has an index the datetime of when it was created.
271+* Each structural node in a book has a constant index fragment, either `%meta` for the metadata revision container node,
272+ or `%comments` for the comments container node.
273+* Every comment has an index of the datetime of when it was posted.
274+* The metadata revisions are a single incrementing number starting from 1, so the initial metadata has a revision count of `1`, and a subsequent edit has a revision count of `2`, and so on.
275+
276+### Schema Enforcement
277+
278+Recall that when using `%graph-store`, all data that is to be added to a graph passes through our [custom validator](https://github.com/ynx0/library/blob/master/mar/graph/validator/library.hoon).
279+Take a moment to read through it and cross-check your understanding with the following summary.
280+
281+**Validator logic summary**
282+
283+- None of the structural nodes require data, so we simply assert that their contents are empty
284+- For a *Specific Comment Node*, a post with an index matching the structure `[@ %comments @ ~]`, we assert that `contents` only contains a single `%text` content.
285+ This choice is arbitrary but meant to keep the application simple and so that a potential frontend does not have to do complex rendering.
286+- For a *Specific Metadata Revision*, a post with index matching the structure `[@ %meta @ ~]`, we first ensure that it's contents only contain two `%text` content instances.
287+ We then use two helper arms `+is-title-valid` and `+is-isbn-valid` to validate the two values. For us, any title value is accepted, while only strings with length 10 or 13 are valid ISBNs.
288+ (We don't do any true ISBN validation for simplicity's sake)
289+
290+## Access Control
291+
292+### Rules
293+
294+There are explicit access control rules called `policy`s, (defined [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/sur/library.hoon#L45-L49))
295+which are set by the user per-library at the time of creation (stored [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L179)).
296+These specify who can or cannot gain access to a library.
297+
298+Here's what `policy` looks like:
299+```
300++$ policy
301+ $% [%open ~] :: any ship is allowed
302+ [%children ~] :: any children (i.e. moons) are allowed
303+ [%whitelist ships=(set ship)] :: any ships in the provided set are allowed
304+ ==
305+```
306+
307+There are also implicit rules, defined as follows:
308+
309+For any given library that one owns,
310+- An owner can:
311+ + add a top level book node to any library
312+ + add a metadata revision to any book
313+ + add or remove any comment on any book
314+- A reader, that is, a another ship granted access to the library, can:
315+ + add a comment to any book that they have requested
316+ + remove any comment that they are the author of
317+
318+Implicitly, all readers are given permission to get any book when granted access to the library.
319+
320+### Implementation
321+
322+- `policies` is a `(map @tas policy)`. That is, a map between the names of libraries that we own and what policy should be enforced on each one and is a part of the agent state (shown [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L13)).
323+
324+- The `+is-allowed` arm, (found [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/lib/library.hoon#L21))
325+ implements each `policy`'s behavior, and is reproduced below.
326+ ```
327+ ++ is-allowed
328+ |= [requester=ship host=ship =policy:library]
329+ ^- ?
330+ ?: =(requester host) :: host is always allowed
331+ %.y
332+ ?- -.policy
333+ %open %.y
334+ %children (team:title host requester)
335+ %whitelist (~(has in ships.policy) requester)
336+ ==
337+ ```
338+ Given the host ship of a resource and the policy to be enforced for a resource, `+is-allowed` returns a boolean for whether or not a requesting ship should be allowed access to that resource.
339+
340+
341+- `+is-allowed` is used in `+on-watch` [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L113), where it only allows a ship to subscribe to a library if it passes the permissions check.
342+- It is also used [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L299) and [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L310), so that when a ship wants to know about what libraries and books exist, only data they are allowed to see gets revealed to them.
343+- Some of the more ad-hoc/implicit permission rules are implemented at the following locations: [[1](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L318) [2](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L48) [3](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L256) [4](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L299)]
344+
345+## Alternative Methods
346+
347+There are a number of architectural decisions made in our application that may not be suitable for other projects.
348+Many decisions made such as the definition of the schema, the user-facing and inter-proxy API, and the permissioning model could be done differently.
349+For instance, many decisions in the schema and thus the validator were made to keep the schema simple. One could imagine allowing any content type and amount,
350+while right now it is restricted to a single text content, and stricter validation of or a custom for the ISBN.
351+The subscription model is also just one way to handle subscriptions and by no means the only one.
352+The permissioning scheme was also implemented relatively simple; it has some limitations, like the fact that you cannot change the permissions after creating the a library, or the fact that there are only 3 policy types.
353+
354+### Comparison between `%library-proxy` and `%graph-push-hook`
355+
356+`%library-proxy` performs the functions of both `%graph-push-hook` and `%graph-pull-hook`.
357+`%graph-pull-hook` is responsible for asking other `%graph-push-hook`s for graph data
358+by trying to initiate a subscription on the host ship's `%graph-push-hook`, which the host only allows if they have permissions.
359+When it succeeds, `%graph-push-hook` then sends out all `graph-updates` to those subscribers who need to hear about it.
360+`%graph-pull-hook` then merges in graph updates it hears into the local `%graph-store`.
361+So far, all the functionality is in line with `%library-proxy`.
362+
363+The two main places where the hooks differ is in their choice of permissioning model and subscription model.
364+To know whether a person is allowed to access a resource, pull hook uses checks to see whether they are a member of a group, while `%library-proxy` uses its own policy type.
365+For subscriptions, the hooks do it per-resource, (i.e. one subscription path per resource, many ships subscribe to that),
366+while library proxy incorporates both the resource and the ship in its path, making the subscription model per-resource per-ship.
367+
368+In general, if your application's use case does not fall neatly with the permissioning and subscription model used by the hooks, you'll need to make your own proxy agent.
369+Otherwise, you will likely want to use the existing hooks and save yourself the trouble of reimplementing all of the functionality that they offer.
content/docs/userspace/overview.md+5
@@ -10,6 +10,11 @@ Urbit's userspace is managed by Arvo's Gall vane.
1010
1111Resources on Gall which manages userspace applications.
1212
13+## [Graph Store](/docs/userspace/graph-store)
14+
15+Resources on Graph Store - the graph database utilized for many Landscape
16+applications and oriented for social media in general.
17+
1318## [Landscape](/docs/userspace/landscape/)
1419
1520Resources on the flagship web client Landcape, through which most users interact with their ship.
content/docs/userspace/threads/basics/summary.md+3−3
@@ -42,16 +42,16 @@ Now here's a quick recap of the main points covered:
4242- are conventionally given the face `m`.
4343- are a core that has three main arms - `form`, `pure` and `bind`:
4444
45−#### form
45+### form
4646
4747- is the mold of the strand suitable for casting
4848- is the type returned by the other arms
4949
50−#### pure
50+### pure
5151
5252- simply returns the `form` of a `strand` that produces pure's argument without doing any IO
5353
54−#### bind
54+### bind
5555
5656- is used to chain strands together like javascript promises
5757- is used in conjunction with micgal (`;<`)
content/docs/userspace/threads/examples/child-thread.md+33−33
@@ -4,31 +4,31 @@ weight = 2
44template = "doc.html"
55+++
66
7−Here's a simple example of a thread that starts another thread:
7+Here's a simple example of a thread that starts another thread:
88
9−### parent.hoon
9+#### `parent.hoon`
1010
1111```hoon
1212/- spider
1313/+ *strandio
1414=, strand=strand:spider
15−^- thread:spider
16−|= arg=vase
17−=/ m (strand ,vase)
15+^- thread:spider
16+|= arg=vase
17+=/ m (strand ,vase)
1818^- form:m
1919;< tid=tid:spider bind:m (start-thread %child)
2020(pure:m !>(~))
2121```
2222
23−### child.hoon
23+#### `child.hoon`
2424
2525```hoon
2626/- spider
2727/+ *strandio
2828=, strand=strand:spider
29−^- thread:spider
30−|= arg=vase
31−=/ m (strand ,vase)
29+^- thread:spider
30+|= arg=vase
31+=/ m (strand ,vase)
3232^- form:m
3333%- (slog leaf+"foo" ~)
3434(pure:m !>(~))
@@ -48,7 +48,7 @@ foo
4848;< tid=tid:spider bind:m (start-thread %child)
4949```
5050
51−See here how we gave `start-thread` the name of the thread to run. It returns the `tid` of the thread, which we could then use to poke it or whatever.
51+See here how we gave `start-thread` the name of the thread to run. It returns the `tid` of the thread, which we could then use to poke it or whatever.
5252
5353`start-thread` handles creating the `tid` for the thread so is quite convenient.
5454
@@ -58,15 +58,15 @@ Note that threads we start this way will be a child of the thread that started t
5858
5959If we want to actually get the result of the thread we started, it's slightly more complicated:
6060
61−### parent.hoon
61+#### `parent.hoon`
6262
6363```hoon
6464/- spider
6565/+ *strandio
6666=, strand=strand:spider
67−^- thread:spider
68−|= arg=vase
69−=/ m (strand ,vase)
67+^- thread:spider
68+|= arg=vase
69+=/ m (strand ,vase)
7070^- form:m
7171;< =bowl:spider bind:m get-bowl
7272=/ tid `@ta`(cat 3 'strand_' (scot %uv (sham %child eny.bowl)))
@@ -84,7 +84,7 @@ If we want to actually get the result of the thread we started, it's slightly mo
8484==
8585```
8686
87−### child.hoon
87+#### `child.hoon`
8888
8989```hoon
9090/- spider
@@ -94,9 +94,9 @@ If we want to actually get the result of the thread we started, it's slightly mo
9494|%
9595++ url "https://www.whatsthelatestbasehash.com/"
9696--
97−^- thread:spider
98−|= arg=vase
99−=/ m (strand ,vase)
97+^- thread:spider
98+|= arg=vase
99+=/ m (strand ,vase)
100100^- form:m
101101;< =cord bind:m (fetch-cord url)
102102=/ hash-as-cord `@t`(end [3 (sub (met 3 cord) 1)] cord)
@@ -104,7 +104,7 @@ If we want to actually get the result of the thread we started, it's slightly mo
104104(pure:m !>(hash))
105105```
106106
107−`child.hoon` simply grabs the latest base hash from https://www.whatsthelatestbasehash.com/ and returns it.
107+`child.hoon` simply grabs the latest base hash from https://www.whatsthelatestbasehash.com/ and returns it.
108108
109109`parent.hoon` is a bit more complicated so we'll look at it line-by-line
110110
@@ -136,10 +136,10 @@ We pre-emptively subscribe for the result. Spider sends the result at `/thread-r
136136
137137Spider takes a poke with a mark %spider-start and a vase containing `[parent=(unit tid) use=(unit tid) file=term =vase]` to start a thread, where:
138138
139−* `parent` is an optional parent thread. In this case we say the parent is our tid. Specifying a parent means the child will be killed if the parent ends.
140−* `use` is the thread ID for the thread we're creating
141−* `file` is the filename of the thread we want to start
142−* `vase` is the vase it will be given as an argument when it's started
139+- `parent` is an optional parent thread. In this case we say the parent is our tid. Specifying a parent means the child will be killed if the parent ends.
140+- `use` is the thread ID for the thread we're creating
141+- `file` is the filename of the thread we want to start
142+- `vase` is the vase it will be given as an argument when it's started
143143
144144```hoon
145145;< =cage bind:m (take-fact /awaiting/[tid])
@@ -164,15 +164,15 @@ Finally we test whether the thread produced a `%thread-done` or a `%thread-fail`
164164
165165## Stop a thread
166166
167−### parent.hoon
167+#### `parent.hoon`
168168
169169```hoon
170170/- spider
171171/+ *strandio
172172=, strand=strand:spider
173−^- thread:spider
174−|= arg=vase
175−=/ m (strand ,vase)
173+^- thread:spider
174+|= arg=vase
175+=/ m (strand ,vase)
176176^- form:m
177177;< =bowl:spider bind:m get-bowl
178178=/ tid `@ta`(cat 3 'strand_' (scot %uv (sham %child eny.bowl)))
@@ -193,7 +193,7 @@ Finally we test whether the thread produced a `%thread-done` or a `%thread-fail`
193193(pure:m !>("Done"))
194194```
195195
196−### child.hoon
196+#### `child.hoon`
197197
198198```hoon
199199/- spider
@@ -213,8 +213,8 @@ Finally we test whether the thread produced a `%thread-done` or a `%thread-fail`
213213 ==
214214--
215215^- thread:spider
216−|= arg=vase
217−=/ m (strand ,vase)
216+|= arg=vase
217+=/ m (strand ,vase)
218218^- form:m
219219;< ~ bind:m looper
220220(pure:m !>(~))
@@ -232,6 +232,6 @@ Finally we test whether the thread produced a `%thread-done` or a `%thread-fail`
232232 ==
233233```
234234
235−* `%spider-stop` is the mark that tells spider to kill a thread.
236−* `tid` is the tid of the thread to kill
237−* `%.y` tells spider to suppress traceback in the result of the killed thread. If you give it `%.n` it will include the traceback.
235+- `%spider-stop` is the mark that tells spider to kill a thread.
236+- `tid` is the tid of the thread to kill
237+- `%.y` tells spider to suppress traceback in the result of the killed thread. If you give it `%.n` it will include the traceback.
content/docs/userspace/threads/examples/get-json.md+5−4
@@ -4,18 +4,19 @@ weight = 1
44template = "doc.html"
55+++
66
7−Grabbing JSON from some url is very easy.
7+Grabbing JSON from some url is very easy.
88
99`strandio` includes the `fetch-json` function which will handle the http request and response parsing and just return unformatted json data.
1010
11−Here's a simple thread that will:
11+Here's a simple thread that will:
12+
1213- grab JSON of the latest base-hash from https://www.whatsthelatestbasehash.com/latesthash.json
1314- grab JSON of a list of all base-hashes with version info from https://www.whatsthelatestbasehash.com/all.json
1415- format and cast them
1516- check `all.json` includes the latest base-hash
1617- print the details of the latest base-hash to the dojo
1718
18−### latest-hash.hoon
19+#### `latest-hash.hoon`
1920
2021```hoon
2122/- spider
@@ -86,7 +87,7 @@ Release: https://github.com/urbit/urbit/releases/tag/urbit-os-v2.39
8687~
8788```
8889
89−## Analysis
90+### Analysis
9091
9192`fetch-json` takes a url as a tape, so we've added a core with the urls like:
9293
content/docs/userspace/threads/examples/main-loop.md+8−8
@@ -18,7 +18,7 @@ This is useful if you want to (for example) take an arbitrary number of facts.
1818
1919Here's an example of a thread that subscribes to `graph-store` for updates and nicely prints the messages (an extremely basic chat reader):
2020
21−#### chat-watch.hoon
21+#### `chat-watch.hoon`
2222
2323```hoon
2424/- spider
@@ -111,15 +111,15 @@ baz
111111~
112112```
113113
114−#### tester.hoon
114+#### `tester.hoon`
115115
116116```hoon
117117/- spider
118118/+ *strandio
119119=, strand=strand:spider
120−^- thread:spider
121−|= arg=vase
122−=/ m (strand ,vase)
120+^- thread:spider
121+|= arg=vase
122+=/ m (strand ,vase)
123123^- form:m
124124;< tid=tid:spider bind:m (start-thread %tested)
125125;< our=ship bind:m get-our
@@ -129,7 +129,7 @@ baz
129129(pure:m !>(~))
130130```
131131
132−#### tested.hoon
132+#### `tested.hoon`
133133
134134```hoon
135135/- spider
@@ -163,9 +163,9 @@ baz
163163 (pure:m ~)
164164 ==
165165--
166−^- thread:spider
166+^- thread:spider
167167|= arg=vase
168−=/ m (strand ,vase)
168+=/ m (strand ,vase)
169169^- form:m
170170;< ~ bind:m looper
171171(pure:m !>(~))
content/docs/userspace/threads/examples/poke-agent.md+2−2
@@ -6,7 +6,7 @@ template = "doc.html"
66
77Here's a thread that lets you post a message to a chat in graph-store:
88
9−#### post-msg.hoon
9+#### `post-msg.hoon`
1010
1111```hoon
1212/- spider
@@ -55,6 +55,6 @@ Save it in `/ted`, `|commit`, and run it like:
5555
5656(obviously change the channel name to whatever you have)
5757
58−## Analysis
58+### Analysis
5959
6060Pretty simple, just use `on-poke` with an argument of `[ship term] cage` where `term` is the agent and `cage` is whatever the particular agent expects.
content/docs/userspace/threads/examples/scry.md+10−10
@@ -6,12 +6,12 @@ template = "doc.html"
66
77Here's an example of a thread that scries ames for the IP address & port of a ship and nicely prints it:
88
9−#### get-ip.hoon
9+#### `get-ip.hoon`
1010
1111```hoon
1212/- spider
1313/+ strandio
14−=, strand=strand:spider
14+=, strand=strand:spider
1515=, strand-fail=strand-fail:libstrand:spider
1616|%
1717++ process-lanes
@@ -30,9 +30,9 @@ Here's an example of a thread that scries ames for the IP address & port of a sh
3030 %- (slog leaf+"{ip}:{port}" ~)
3131 (pure:m ~)
3232--
33−^- thread:spider
33+^- thread:spider
3434|= arg=vase
35−=/ m (strand ,vase)
35+=/ m (strand ,vase)
3636^- form:m
3737=/ utarget !< (unit @p) arg
3838?~ utarget
@@ -51,20 +51,20 @@ Save as `ted/get-ip.hoon`, `|commit %home`, and run it with `-get-ip ~bitbet-bol
515134.83.113.220:60659
5252```
5353
54−## Analysis
54+### Analysis
5555
5656Here we use the `strandio` function `scry` which takes an argument of `[mold path]` where:
5757
5858- `mold` is the return type of the scry
5959- `path` is the scry path formatted like:
60− 1. vane letter and care
61− 2. desk if scrying arvo or agent if scrying a gall agent
62− 3. rest of path
63−
60+ 1. vane letter and care
61+ 2. desk if scrying arvo or agent if scrying a gall agent
62+ 3. rest of path
63+
6464In our case the mold is `(list lane:ames)` and the path is `/ax//peers/(scot %p target)/forward-lane` like:
6565
6666```hoon
6767;< lanes=(list lane:ames) bind:m (scry:strandio (list lane:ames) /ax//peers/(scot %p target)/forward-lane)
6868```
6969
70−After that we just process the result in `++ process-lanes` and print it.
70+After that we just process the result in `++ process-lanes` and print it.
content/docs/userspace/threads/examples/take-fact.md+2−2
@@ -6,7 +6,7 @@ template = "doc.html"
66
77Taking a fact from an agent, arvo or whatever is easy. First you subscribe using `watch:strandio` or `watch-our:strandio`, then you use `take-fact:strandio` to receive the fact. Here's an example that takes an update from `graph-store` and prints the message to the dojo:
88
9−#### print-msg.hoon
9+#### `print-msg.hoon`
1010
1111```hoon
1212/- spider
@@ -44,7 +44,7 @@ Taking a fact from an agent, arvo or whatever is easy. First you subscribe using
4444
4545Create a chat on your fake zod if you don't have one already, then save it in `/ted`, `|commit %home`, and run `-print-msg`. Next, type some message in your chat and you'll see it printed in the dojo.
4646
47−## Analysis
47+### Analysis
4848
4949First we call `watch-our` to subscribe:
5050
content/docs/userspace/threads/gall/poke-thread.md+8−9
@@ -6,7 +6,7 @@ template = "doc.html"
66
77Here's a modified agent that pokes our thread. I've replaced some off the previous stuff because it was getting a little unwieldly.
88
9−#### thread-starter.hoon
9+#### `thread-starter.hoon`
1010
1111```hoon
1212/+ default-agent, dbug
@@ -70,15 +70,15 @@ Here's a modified agent that pokes our thread. I've replaced some off the previo
7070
7171And here we've modified the thread to take the poke and return it as the result:
7272
73−#### test-thread.hoon
73+#### `test-thread.hoon`
7474
7575```hoon
76−/- spider
76+/- spider
7777/+ *strandio
78−=, strand=strand:spider
79−^- thread:spider
80−|= arg=vase
81−=/ m (strand ,vase)
78+=, strand=strand:spider
79+^- thread:spider
80+|= arg=vase
81+=/ m (strand ,vase)
8282^- form:m
8383;< vmsg=vase bind:m (take-poke %foo)
8484(pure:m vmsg)
@@ -86,13 +86,12 @@ And here we've modified the thread to take the poke and return it as the result:
8686
8787Save them, `|commit` and run it like `:thread-starter [%test-thread %blah]`. You should see:
8888
89−
9089```
9190Result: blah
9291> :thread-starter [%test-thread %blah]
9392```
9493
95−## Analysis
94+### Analysis
9695
9796In our agent we've added this card:
9897
content/docs/userspace/threads/gall/start-thread.md+9−9
@@ -6,7 +6,7 @@ template = "doc.html"
66
77Here's an example of a barebones gall agent that just starts a thread:
88
9−#### thread-starter.hoon
9+#### `thread-starter.hoon`
1010
1111```hoon
1212/+ default-agent, dbug
@@ -20,7 +20,7 @@ Here's an example of a barebones gall agent that just starts a thread:
2020++ on-init on-init:def
2121++ on-save on-save:def
2222++ on-load on-load:def
23−++ on-poke
23+++ on-poke
2424 |= [=mark =vase]
2525 ^- (quip card _this)
2626 ?+ mark (on-poke:def mark vase)
@@ -60,14 +60,14 @@ Here's an example of a barebones gall agent that just starts a thread:
6060
6161And here's a minimal thread to test it with:
6262
63−#### test-thread.hoon
63+#### `test-thread.hoon`
6464
6565```hoon
66−/- spider
67−=, strand=strand:spider
68−^- thread:spider
69−|= arg=vase
70−=/ m (strand ,vase)
66+/- spider
67+=, strand=strand:spider
68+^- thread:spider
69+|= arg=vase
70+=/ m (strand ,vase)
7171^- form:m
7272|= strand-input:strand
7373?+ q.arg [~ %fail %not-foo ~]
@@ -109,7 +109,7 @@ Thread failed to start
109109/sys/vane/gall/hoon:<[1.370 9].[1.370 37]>
110110```
111111
112−## Analysis
112+### Analysis
113113
114114We can ignore the input logic, here's the important part:
115115
content/docs/userspace/threads/gall/stop-thread.md+8−8
@@ -6,7 +6,7 @@ template = "doc.html"
66
77Here we've added one last card to `on-poke` to stop the thread and a little extra to `on-agent` to print things for demonstrative purposes.
88
9−#### thread-starter.hoon
9+#### `thread-starter.hoon`
1010
1111```hoon
1212/+ default-agent, dbug
@@ -91,15 +91,15 @@ Here we've added one last card to `on-poke` to stop the thread and a little extr
9191
9292We've also added a `sleep` to the thread to keep it running for demonstration.
9393
94−#### test-thread.hoon
94+#### `test-thread.hoon`
9595
9696```hoon
97−/- spider
97+/- spider
9898/+ *strandio
99−=, strand=strand:spider
100−^- thread:spider
101−|= arg=vase
102−=/ m (strand ,vase)
99+=, strand=strand:spider
100+^- thread:spider
101+|= arg=vase
102+=/ m (strand ,vase)
103103^- form:m
104104;< =path bind:m take-watch
105105;< ~ bind:m (send-raw-card [%give %fact ~[path] %update !>("message 1")])
@@ -141,7 +141,7 @@ Thread cancelled successfully
141141Thread failed: cancelled
142142```
143143
144−## Analysis
144+### Analysis
145145
146146The card we've added to our agent:
147147
content/docs/userspace/threads/gall/take-facts.md+8−8
@@ -8,7 +8,7 @@ Most of the time you'll just want the final result like how we did previously. S
88
99Here we've added another card to subscribe for any facts sent by the thread and some small changes to `on-agent`:
1010
11−#### thread-starter.hoon
11+#### `thread-starter.hoon`
1212
1313```hoon
1414/+ default-agent, dbug
@@ -80,15 +80,15 @@ Here we've added another card to subscribe for any facts sent by the thread and
8080
8181We've also made some changes to the thread:
8282
83−#### test-thread.hoon
83+#### `test-thread.hoon`
8484
8585```hoon
86−/- spider
86+/- spider
8787/+ *strandio
88−=, strand=strand:spider
89−^- thread:spider
90−|= arg=vase
91−=/ m (strand ,vase)
88+=, strand=strand:spider
89+^- thread:spider
90+|= arg=vase
91+=/ m (strand ,vase)
9292^- form:m
9393;< =path bind:m take-watch
9494;< ~ bind:m (send-raw-card [%give %fact ~[path] %update !>("message 1")])
@@ -127,7 +127,7 @@ message 4
127127Thread failed: not-foo
128128```
129129
130−## Analysis
130+### Analysis
131131
132132In our agent's `on-poke` arm we've added another card to subscribe to `/thread/[tid]/updates`:
133133
content/docs/userspace/threads/gall/take-result.md+9−9
@@ -6,7 +6,7 @@ template = "doc.html"
66
77Here we've added an extra card to subscribe for the result and a couple of lines in on-agent to test if it succeeded:
88
9−#### thread-starter.hoon
9+#### `thread-starter.hoon`
1010
1111```hoon
1212/+ default-agent, dbug
@@ -71,14 +71,14 @@ Here we've added an extra card to subscribe for the result and a couple of lines
7171--
7272```
7373
74−#### test-thread.hoon
74+#### `test-thread.hoon`
7575
7676```hoon
77−/- spider
78−=, strand=strand:spider
79−^- thread:spider
80−|= arg=vase
81−=/ m (strand ,vase)
77+/- spider
78+=, strand=strand:spider
79+^- thread:spider
80+|= arg=vase
81+=/ m (strand ,vase)
8282^- form:m
8383|= strand-input:strand
8484?+ q.arg [~ %fail %not-foo ~]
@@ -101,9 +101,9 @@ Thread started successfully
101101Thread failed: not-foo
102102```
103103
104−## Analysis
104+### Analysis
105105
106−In `on-poke` we've added an extra card *before* the `%spider-start` poke to subscribe for the result:
106+In `on-poke` we've added an extra card _before_ the `%spider-start` poke to subscribe for the result:
107107
108108```hoon
109109[%pass /thread/[ta-now] %agent [our.bowl %spider] %watch /thread-result/[tid]]
content/docs/userspace/threads/http-api.md+1−1
@@ -26,7 +26,7 @@ When Spider receives an HTTP request, the following steps happen:
2626
2727Thus, it's important to understand that the original HTTP request and final HTTP response must contain JSON data, and therefore the input & output `mark`s you specify must each have a `mark` file in `/mar` that includes a conversion method for `json -> inputMark` and `outputMark -> json` respectively.
2828
29−# Example
29+## Example
3030
3131Here we'll look at running Spider threads through Eyre.
3232
content/docs/userspace/threads/reference.md+5−13
@@ -4,15 +4,7 @@ weight = 50
44template = "doc.html"
55+++
66
7−## Spider
8−
9−- [Start a thread](#start-thread)
10−- [Stop a thread](#stop-thread)
11−- [Subscribe to thread result](#subscribe-to-thread-result)
12−- [Subscribe to thread](#subscribe-to-thread)
13−- [Poke a thread](#poke-thread)
14−
15−### Start thread
7+## Start thread
168
179Poke `spider` with mark `%spider-start` and a vase containing `start-args`:
1810
@@ -34,7 +26,7 @@ Where:
3426[%pass /some-path %agent [our.bowl %spider] %poke %spider-start !>([~ `tid %foo !>(~)])]
3527```
3628
37−### Stop thread
29+## Stop thread
3830
3931Poke `spider` with mark `%spider-stop` and a vase containing `[tid ?]`, where:
4032
@@ -47,7 +39,7 @@ Poke `spider` with mark `%spider-stop` and a vase containing `[tid ?]`, where:
4739[%pass /some-path %agent [our.bowl %spider] %poke %spider-stop !>([tid %.y)]
4840```
4941
50−### Subscribe to thread result
42+## Subscribe for result
5143
5244Spider will send the result on `/thread-result/[tid]` so you can subscribe there for the result. You should subscribe before starting the thread.
5345
@@ -62,7 +54,7 @@ The result will have a mark of either `%thread-fail` or `%thread-done`.
6254[%pass /some-path %agent [our.bowl %spider] %watch /thread-result/[tid]]
6355```
6456
65−### Subscribe to thread
57+## Subscribe to thread
6658
6759You can subscribe to a thread on `/thread/[tid]/path`. Note this is for facts sent off by the thread while it's running, not the final result. The path depends on the particular thread.
6860
@@ -72,7 +64,7 @@ You can subscribe to a thread on `/thread/[tid]/path`. Note this is for facts se
7264[%pass /some-path %agent [our.bowl %spider] %watch /thread/[tid]/thread-path]
7365```
7466
75−### Poke Thread
67+## Poke thread
7668
7769To poke a thread you poke spider with a mark of `%spider-input` and a vase of `[tid cage]`.
7870
content/events/2021-09-16-developer-call-azimuth-scaled.mdnew file+46
@@ -0,0 +1,46 @@
1++++
2+title = "Developer Call: Azimuth, Scaled"
3+starts = "2021-09-16T09:00:00"
4+ends = "2021-09-16T10:00:00"
5+timezone = "America/Los_Angeles"
6+location = "Online Event"
7+image = ""
8+registration_url = "https://www.meetup.com/urbit-sf/events/280697973/"
9+description = "Discussion of Layer 2 Architecture and Implementation"
10+dark = false
11+youtube = "UScbZ6n7H-4"
12+
13+[[hosts]]
14+name = "Philip Monk"
15+patp = "~wicdev-wisryt"
16+
17+[[guests]]
18+name = "Jimmy Young"
19+patp = "~fallyn-balfus"
20+
21+[[guests]]
22+name = " Jose Cisneros"
23+patp = "~norsyr-torryn"
24+
25+[[guests]]
26+name = " Tom Holford"
27+patp = "~datder-sonnet"
28+
29+[[guests]]
30+name = " Mark"
31+patp = "~palfun-foslup"
32+
33+[[guests]]
34+name = " Poprox"
35+patp = "~datnut-pollen"
36+
37+[[guests]]
38+name = " Will Galebach"
39+patp = "~fabnev-hinmur"
40+
41+
42++++
43+
44+ETH Gas prices are too high! The growth of Urbit has been greatly hampered this year by the cost of activating planets. Join Philip Monk (`~wicdev-wisryt`) and the entire L2 team as they present the work completed to date to implement Layer 2 naive rollups. Come see a demo of the new Bridge front end, first migrating a star and spawning some planets, then activating a planet, all on L2.
45+
46+This call will be a great opportunity for galaxy owners to get their questions answered for the upcoming vote on Layer 2 implementation. It will also be good for anyone in the community that wants a comprehensive look at how naive rollups will work to bring down planet activation costs. If you're technically inclined, be sure to check out [Philip's walkthrough](https://www.youtube.com/watch?v=lQdqCwYIBoU) of the naive rollup code ahead of time so you can ask questions.
content/events/2021-09-24-urbit-outside.mdnew file+23
@@ -0,0 +1,23 @@
1++++
2+title = "Urbit Outside"
3+starts = "2021-09-24T15:00:00"
4+ends = "2021-09-24T19:00:00"
5+timezone = "America/Los_Angeles"
6+location = "Sibley Staging Area, 6800 Skyline Blvd. Oakland, CA"
7+image = "https://secure.meetupstatic.com/photos/event/3/0/5/7/clean_498732375.jpeg"
8+registration_url = "https://www.meetup.com/urbit-sf/events/280696590/?isFirstPublish=true"
9+description = "Urbit but outside"
10+dark = true
11++++
12+
13+![](https://secure.meetupstatic.com/photos/event/3/0/5/7/clean_498732375.jpeg)
14+
15+For our second post-Covid Urbit IRL, we're heading to the East Bay!
16+
17+Please join us at Sibley Preserve for a hangout and (optional) hike.
18+
19+We'll meet at the Sibley Staging Area at 3:00PM, and leave for a casual walk to the caldera and labyrinth at 4:00PM.
20+
21+Hiking shoes, snacks, water, and picnic blankets/chairs are encouraged! There are restrooms and water at the Staging Area.
22+
23+Please join the channel 'Urbit Outside ~[masked]' in ~bitbet-bolbel/urbit-community to get in touch with the gang.
content/events/2021-10-15-assembly.mdnew file+26
@@ -0,0 +1,26 @@
1++++
2+title = "Assembly"
3+starts = "2021-10-15T15:30:00"
4+ends = "2021-10-17T18:00:00"
5+timezone = "America/Chicago"
6+location = "Distribution Hall, 1500 East 4th St. Austin, TX"
7+image = "https://storage.googleapis.com/media.urbit.org/site/events/assembly-image.png"
8+registration_url = "http://assembly.urbit.org/"
9+description = "Grow a new internet"
10+dark = true
11++++
12+
13+![](https://storage.googleapis.com/media.urbit.org/site/events/assembly-event-image.png)
14+
15+Assembly 2021 marks a pivotal moment in Urbit: software distribution. Yes, Urbit is ready to be built upon. We’ll spend three days together doing just that.
16+
17+- Learn about building applications on Urbit (it’s easy).
18+- Find out how to work and create on a platform that is fully owned by you.
19+- Meet with the growing ecosystem of communities and businesses built on Urbit.
20+
21+If you’re simply into calm computing, well, there will be plenty of that too.
22+
23+You’ll choose between two tracks:
24+
25+- Assembly: Assemble software or work together on projects in small groups
26+- Workshop: A two day workshop taught by Neal Davis, Urbit for Developers
content/getting-started/planet.md+4−2
@@ -26,8 +26,10 @@ Using hosting does mean that you're trusting your provider with your data, but s
2626
2727Current hosting providers are:
2828
29−- [Tlon Corporation](https://tlon.io): Tlon is the creator of Landscape and is primarily responsible for the creation and maintenance of the Urbit project itself.
30−- [Get an Urbit ID](https://www.geturbitid.com/): Get an Urbit ID was the first hosting provider and is run by a member of the Urbit community.
29+- [escape pod store](https://www.escapepod.store/)
30+- [Third Earth](https://third.earth/)
31+- [Tlon Corporation](https://tlon.io)
32+- [UrbitHost](https://urbithost.com)
3133
3234### Choose to run your planet on your computer or on a cloud service
3335
content/grants/a-collaborative-web-mapping-system-for-urbit.md → content/grants/a-network-enabled-spatial-database-and-demonstration-app.mdrenamed+26−21
@@ -1,5 +1,5 @@
11+++
2−title = "A Collaborative Web Mapping System for Urbit"
2+title = "A network enabled spatial database and demonstration app for Urbit"
33date = "2020-09-02"
44
55[taxonomies]
@@ -16,7 +16,7 @@ completed = false
1616link = ""
1717+++
1818
19−# A Collaborative Web Mapping System for Urbit
19+# A network enabled spatial database and demonstration app for Urbit
2020
2121##### Goal
2222
@@ -24,15 +24,15 @@ Allow small scale spatial data to be stored and retrieved in Urbit, for use in l
2424
2525##### Background/Motivation
2626
27−This proposal has grown out of a previous bounty which called for Open Street Map data to be ingested into Urbit. Through discussion it was decided that as OSM already provides a free, open and widely available base map and street network, a more useful system for Urbit would provide a method of storing spatial documents, which can overlay upon existing spatial data such as Open Street Map tiles, be collected together for use in landscape apps, and combined, shared and collaboratively edited with other ships as 'Portals'. A spatial document, for the purpose of this proposal, is a single valid GeoJSON[^1] document. A spatial document could contain a single geometry, a feature (geometry with associated properties), or a collection of features (a GeoJSON 'featurecollection'). A Portal is a collection of spatial documents, with associated metadata and can include spatial documents from multiple ships, whose owners can collaboratively edit or construct maps.
27+This proposal has grown out of a previous bounty which called for Open Street Map data to be ingested into Urbit. Through discussion it was decided that as OSM already provides a free, open and widely available base map and street network, a more useful system for Urbit would provide a method of storing spatial documents, which can overlay upon existing spatial data such as Open Street Map tiles, be collected together for use in landscape apps, ...and poast .... A spatial document, for the purpose of this proposal, is a single valid GeoJSON[^1] document. A spatial document could contain a single geometry, a feature (geometry with associated properties), or a collection of features (a GeoJSON 'featurecollection'). The fridge store is a collection of poastcards, both those stored locally and those shared from other ships.
2828
2929##### Overview
3030
3131- The proposed system will follow the Urbit store-hook-view pattern as described here https://docs.google.com/document/d/1hS_UuResG1S4j49_H-aSshoTOROKBnGoJAaRgOipf54/edit?ts=5d533e42
3232
33−- The system will be implemented in two stages. The first stage will implement simple spatial types and a spatial document store, demonstrate input and output of spatial data, and provide a minimal landscape app for creating and editing spatial documents, but limited to the local ship. The second stage will build on the first and provide Portals; collections of spatial documents and associated metadata, which can be shared across ships - similar to the 'canvas' landscape app (https://github.com/yosoyubik/canvas).
33+- The system will be implemented in two milestones. The first milestone will implement simple spatial types and a spatial document store, demonstrate input and output of spatial data, and provide a minimal landscape app for creating and editing spatial documents, but limited to the local ship. The second milestone will build on the first and provide a network enabled store and web application for sharing 'poastcards'; spatial documents with text and an image, which can be shared with other ships and viewed through the web application ('Poast').
3434
35−#### Stage 1 - A spatial store, spatial types, simple demo app
35+#### Milestone 1 - A spatial store, spatial types, simple demo app
3636
3737##### Spatial Document Store and Tools
3838
@@ -42,28 +42,32 @@ This proposal has grown out of a previous bounty which called for Open Street Ma
4242- Implement a generator which can read a GeoJSON document in to the store, and can write a constructed document out of the store.
4343- Implement a test suite based around GeoJSON specification and the above generator.
4444
45−##### Hook, View and Landscape App
45+##### Hook, View and (not Landscape) App
4646
4747- Implement the hook and view components of store-hook-view pattern atop the spatial data store.
48−- In this stage the system will only allow data to be viewed/edited on the local ship.
48+- In this milestone the system will only allow data to be viewed/edited on the local ship.
4949- Integrating spatial information with Landscape apps (which can render vector features using available javascript libraries/clients such as Openlayers and Leaflet).
5050- Overall, somewhat like 'urlayers' hackathon project (github.com/gusmacaulay/urlayers), but with a sane data store and following the store-hook-view pattern.
5151
52−#### Stage 2 - A Portal Store and Collaboration System
52+#### Milestone 2 - A networked spatial store, and full fledged demo app
5353
54−##### Store (Metadata, Portals)
54+##### Network enabled store ('Fridge')
5555
56−- Expands on the Stage 1 architecture with additional store
57−- The additional store which will house Portals. Portals consists of a map document with metadata (area of interest bounds, styling[^3], author/s etc.), and which may contain many spatial documents, which may be stored locally or on other ships.
58−- It may be that the Urbit graph store is the appropriate place for this information, exploring using graph store vs a custom Portal store will be undertaken as part of Stage 2.
56+- Expands on the Stage 1 architecture with an enhanced store ('Fridge') and an app ('Poast').
57+- The enhanced store (a 'Fridge'), which will house 'Poastcards'. Poastcards consist of an image,text, a geometry. A poastcard can be represented in a geojson feature, making heavy use of the the feature properties (a key value object, part of the geojson specification) to store the non-spatial information.
58+- It may be that the Urbit graph store is the appropriate way to share/link Poastcards, exploring using graph store vs a custom Fridge store will be undertaken as part of Stage 2.
59+- 'Poast' is a PoastCard viewing and sharing app, for landscape. We will implement a mvp/polished demo for this milestone.
60+- 'Poast' will be made available through the upcoming software distribution system as part of this milestone.
5961
60−##### Hook/View/App
61−
62−- A hook/view/landscape app which allows for displaying and interacting with Portals;
63− - Editing of individual Portals
64− - Sharing of Portals. As a resource owned by a ship, a Portal is somewhat analogous to a chat channel. Individual spatial documents within the Portal can come from local or foreign ship. The owner of the Portal will be able to set permissions on spatial documents within the portal.
65−- The overall concept of the Portal app can be likened to the 'canvas' landscape app, but for maps and cartography. The key feature is to present a Portal map with spatial data from different ships and collaborative editing.
62+##### Rich demonstration App ('Poast')
6663
64+- A hook/view/landscape app which allows for displaying and interacting with Poastcards/Fridge store;
65+ - Editing/Creation of Poastcards via a web ui.
66+ - Sharing of Poastcards; a Poastcard is a resource owned by a ship, once create they can be shared read-only with others.
67+- The overall concept of the Poast app can be likened to the experience of sending postcards and personal letters in the pre internet era. Unlike most present day social media, Poast will not have a feed.
68+- Poast will be minimally landscape integrated, insofar is it can be launched from a tile and follow landscape styling.
69+- For this milestone only a minimal set of features will be included to allow for creating, sharing, and viewing of Poastcards.
70+
6771##### Footnotes
6872
6973[^1]:
@@ -81,12 +85,13 @@ I am a beef farmer and software engineer (geospatial web applications). I work w
8185
8286## Milestones
8387
84−### Stage 1
88+### Milestone 1
8589
86902 stars
8791A spatial store, spatial types, simple demo landscape app
8892
89−### Stage 2
93+### Milestone 2
9094
91952 stars
92−A Portal Store and Collaboration System
96+A network enabled spatial store, for sharing spatial data in urbit.
97+A rich demostration app, for sharing spatial data, images and text.
content/grants/ethereum-abi-parser.md+1
@@ -13,6 +13,7 @@ reward = 2
1313assignee = "xiphiness"
1414id = "1241532070"
1515completed = false
16+canceled = true
1617link = ""
1718+++
1819
content/grants/matrices-in-hoon.md+1−1
@@ -12,7 +12,7 @@ description = "Add array types to Hoon."
1212reward = 5
1313assignee = "N E Davis"
1414id = "611660914"
15−completed = false
15+completed = true
1616link = ""
1717+++
1818
content/grants/~home-urbit.mdnew file+120
@@ -0,0 +1,120 @@
1++++
2+title = "~Home-Urbit"
3+date = "2021-01-01"
4+[taxonomies]
5+grant_type = ["Proposal"]
6+grant_category = ["App Dev", "Dev tool"]
7+[extra]
8+image = "https://user-images.githubusercontent.com/13405632/121785641-98599100-cbc3-11eb-861c-77a95a7db030.png"
9+description = "A turn-key Urbit deployment for your personal server"
10+reward = 4
11+assignee = "~sipsen-pilser"
12+id = ""
13+completed = false
14+link = ""
15++++
16+
17+
18+# Urbit Grant for ~Home-Urbit
19+
20+Home-Urbit is a turn-key solution for self-hosting Urbit
21+
22+
23+Goal: Create a productized version of Home-Urbit with an impecable UX. Setup Urbit on a dedicated piece of hardware (or a virtualized one) with as much less friction as possible.
24+
25+Urbit is your last computer.
26+
27+Vision: Create the software foundations for a dedicated piece of hardware that you can order, your own Urbit. It will enable to easily:
28+1) Install Urbit on the hardware during manufacturing.
29+2) Manage Urbit (and the hw) during usage.
30+
31+Current implementation is based on balenaCloud and balenaOS.
32+
33+[balenaOS](https://www.balena.io/os/?) is a lightweight OS that is crafted for container workloads. It has various optimizations for IoT, including minimal write by the OS on the disk.
34+
35+[balenaCloud](https://www.balena.io/cloud/) is a PaaS to build, deploy and manage IoT workloads. Basically you send a docker-compose project, it is build on their build-servers and the container images are sent to the IoT devices to run. You can manage the whole lifecycle of the app through their web dashboard.
36+
37+balenaCloud has a free tier for up to 10 devices per account, so that's perfect for our ~Home-Urbit application.
38+
39+Moreover, with balenaCloud, users can easily run other applications alongisde Urbit. Whatever application that can run inside a container.
40+
41+The angle is to run the software you need, on your own. With containers that's easier than before. A sovereign individual should own their device, applications and data.
42+
43+Examples:
44+
45+- Netdata monitoring agent
46+- [Radicle seed node](https://radicle.xyz/)
47+- Personal website server
48+- Media center
49+- NAS
50+- etc.
51+
52+
53+## Milestones
54+
55+### Refine current setup (aarch64 architecture) - 1 Star
56+
57+- Fix slow reverse-proxy by nginx (or replace nginx). This is needed so that the user can access Urbit and the various services that are hosted alongside (e.g minio S3) without having to remember ports.
58+- Make ~home-urbit play nice with external SSD. The plan is to install urbit on the SSD and the OS on the sd card. It should give us the speed bost that we are looking for.
59+
60+
61+### Make Home-Urbit compatible with x86 architecture - 1 Star
62+
63+balenaOS is compatible with many different architectures. Make home-urbit compatible with x86, so that users can install balenaOS and run the urbit in computers such as Intel NUCs.
64+
65+A NUC could house multiple Urbits or could enable the user to host many different groups with many users.
66+
67+
68+### Solve the wide accessibility issue - 2 Stars
69+
70+Home-Urbit should be widely accessible for 2 reasons:
71+1) You should be able to access it no matter your location
72+2) MinioS3 demands accessibility by all the parties to be able to view the asset that you linked
73+
74+BalenaCloud offers VPN accessibility that is hardcoded to port 80, doesn't cover requirement 2.
75+
76+Another VPN service would cover both requirements, but not for everybody, just the users of the VPN. It's great to access your device, but not great for others to access your Minio instance.
77+
78+Router forwarding is an option, but it's involved and requires the user to use 3rd-party intructions, since it's different for every router.
79+
80+A universal solution should be found. For example, create a publicly accessible server that sustains reverse `ssh` tunnels to the home-urbit. It simply pipes all the connections that target a specific port to the `home-urbit` at another port. It works as a proxy.
81+
82+Since it's a publicly accessible VM, it solves both requirements. The connection originate from home-urbit itself and it's a tunnel, so there is no need for port forwarding.
83+
84+Alternative solutions will be researched. The chosen solution will be:
85+
86+1) The most reliable
87+2) The easiest
88+3) The fastest
89+
90+We will consider the above attributes in that order of importance.
91+
92+For the upkeep cost, there are a couple of options:
93+1) Discuss with Urbit foundation, generalise solution and offer it to all Urbits. Upkeep cost to be covered by UF.
94+2) Create a simple SaaS with a very cheap free/paid option
95+3) Offer it for free, shoulder cost and use something like Patreon or a similar service.
96+
97+### Make offline version - (> 2 Stars)
98+
99+It is understandable that some users may not want to manage their Urbit via balenaCloud. This milestone will give enable them to install home-urbit in the same* frictionless manner, while doing that offline.
100+
101+Most likely, it will be more invovled than using balenaCloud.
102+
103+It should have feature-parity with the balenaCloud deployment. For example, in place of the balenaCloud's VPN service, there will be an extra container that runs tailscale.
104+
105+3 options:
106+
107+- balenaOS (it's OSS) without the balenaCloud component. You manage it locally via balena-cli.
108+- Other OS angled for running container workloads (e.g bottleRocket )
109+- docker-compose + helper scripts.
110+
111+### Make Content - 2 Stars
112+
113+- Produce all relevant docs
114+- Produce a first-aid handbook (if X, do Y). Talk with Urbit employees and community members to identify causes.
115+- Produce video about the project
116+- Write technical blog post
117+ - Why home-urbit
118+ - Design choices (e.g why containers, why balena). Intro to technologies.
119+ - Running urbit in container, gotchas and bash scripts that were used.
120+ - Tips on running Urbit commands via HTTP
content/using/id/guide-to-breaches.md+1−3
@@ -21,9 +21,7 @@ has forgotten about it.
2121Breaches often fix connectivity issues, but should only be used as a last
2222resort. Before performing a breach, look at alternative fixes in the [Ship
2323Troubleshooting](/using/os/ship-troubleshooting) guide. Also reach out for help in the
24−Help channel in the Urbit Community group `~bitbet-bolbel/urbit-community`, or,
25−failing that, in the `#ship-starting-support` channel in our [Discord
26−server](https://discord.gg/n9xhMdz) to see if there is another option.
24+Help channel in the Urbit Community group `~bitbet-bolbel/urbit-community` to see if there is another option.
2725Connectivity issues are typically related to a bug, and you may be able to help
2826us fix it by emailing us at `support@urbit.org`.
2927
content/using/os/getting-started.md+3−3
@@ -144,16 +144,16 @@ moon: ~faswep-navred-sampel-palnet
144144\/ \/
145145```
146146
147−You must manually edit the output of `|moon` to get the correct format for the `<key>`:
147+You must manually edit the output of `|moon` to get the correct format for the `key`:
148148
149149- strip out the `\/`
150150
151151- combine into one line, omitting spaces
152152
153−`<key>` would be `0w5cT5t.wCO6i.~e1xg.Oz0qb.QNO6I.3Kt2T.h9M9F.U3vU~.X3Qu0.gtb19.IYTkY.80kWZ.SIEUE.DXa8i.TiDof.o3-1C.RHLKS.k81M0.ecz5o.ic0Bg.600g1`
153+`key` would be `0w5cT5t.wCO6i.~e1xg.Oz0qb.QNO6I.3Kt2T.h9M9F.U3vU~.X3Qu0.gtb19.IYTkY.80kWZ.SIEUE.DXa8i.TiDof.o3-1C.RHLKS.k81M0.ecz5o.ic0Bg.600g1`
154154in this example.
155155
156−Put `<key>` in a file and that file becomes `<keyfile>`.
156+Put `key` in a file and that file becomes `keyfile`.
157157
158158You can use the resulting output in the same installation flow from the [Installing Urbit](/getting-started/) guide, following the same scheme as for booting a planet. That scheme is:
159159
content/using/os/ship-troubleshooting.md+2−2
@@ -72,10 +72,10 @@ Personal breaches often fix connectivity issues, but should only be used as a
7272last resort. To find out how to perform a personal breach, check out our [Guide
7373to Breaches](/using/id/guide-to-breaches). Before taking such a drastic measure, try
7474other methods in this guide. You can also ask for help on
75−in the Help channel in the Urbit Community group at `~bitbet-bolbel/urbit-community` or, failing that, in the `#ship-starting-support`
76−channel in our [Discord server](https://discord.gg/n9xhMdz).
75+in the Help channel in the Urbit Community group at `~bitbet-bolbel/urbit-community`.
7776
7877### Reset `+code`
78+
7979**WARNING:** Do not reset your +code if you are using a hosted ship. You will be locked out. Please contact your hosting provider for more information.
8080
8181You login to Landscape using the camel-case phrase obtained from dojo by
lib/constants.js+55−1
@@ -3,8 +3,62 @@ export const contact = {
33 email: "support@urbit.org",
44 twitter: "https://twitter.com/urbit",
55 github: "https://github.com/urbit/urbit",
6− discord: "https://discord.gg/Mae2bk7",
76 urbitCommunity: "~bitbet-bolbel/urbit-community",
87 newsletter:
98 "https://urbit.us11.list-manage.com/subscribe/post?u=972a03db9e0c6c25bb58de8c8&amp;id=be143888d2",
109};
10+
11+export const eventKeys = [
12+ "title",
13+ "ends",
14+ "location",
15+ "image",
16+ "registration_url",
17+ "youtube",
18+ "description",
19+ "starts",
20+ "hosts",
21+ "slug",
22+ "guests",
23+ "content",
24+ "dark",
25+ "timezone",
26+];
27+
28+export const postKeys = [
29+ "title",
30+ "slug",
31+ "date",
32+ "description",
33+ "content",
34+ "extra",
35+];
36+
37+export const grantsKeys = [
38+ "title",
39+ "slug",
40+ "date",
41+ "description",
42+ "content",
43+ "extra",
44+ "taxonomies",
45+];
46+
47+export const mediaKeys = [
48+ "title",
49+ "slug",
50+ "date",
51+ "description",
52+ "content",
53+ "extra",
54+];
55+
56+export const updatesKeys = [
57+ "title",
58+ "slug",
59+ "date",
60+ "description",
61+ "content",
62+ "author",
63+ "ship",
64+];
lib/glossary.js+112−112
@@ -11,21 +11,21 @@ export const glossary = [
1111 name: "dotket",
1212 symbol: ".^",
1313 usage: "Nock",
14− slug: "/docs/hoon/reference/rune/dot/#dotket",
14+ slug: "/docs/hoon/reference/rune/dot/#-dotket",
1515 desc: "<code>[%dtkt p=spec q=hoon]</code>: load from the Arvo namespace with a fake Nock instruction: 'Nock <code>12</code>''.",
1616 },
1717 {
1818 name: "dotlus",
1919 symbol: ".+",
2020 usage: "Nock",
21− slug: "/docs/hoon/reference/rune/dot/#dotlus",
21+ slug: "/docs/hoon/reference/rune/dot/#-dotlus",
2222 desc: "<code>[%dtls p=hoon]</code>: increment an atom with Nock <code>4</code>.",
2323 },
2424 {
2525 name: "dottar",
2626 symbol: ".*",
2727 usage: "Nock",
28− slug: "/docs/hoon/reference/rune/dot/#dottar",
28+ slug: "/docs/hoon/reference/rune/dot/#-dottar",
2929 desc: "<code>[%dttr p=hoon q=hoon]</code>: evaluate with Nock <code>2</code>.",
3030 },
3131 {
@@ -39,7 +39,7 @@ export const glossary = [
3939 name: "dotwut",
4040 symbol: ".?",
4141 usage: "Nock",
42− slug: "/docs/hoon/reference/rune/dot/#dotwut",
42+ slug: "/docs/hoon/reference/rune/dot/#-dotwut",
4343 desc: "<code>[%dtwt p=hoon]</code>: test for cell or atom with Nock <code>3</code>.",
4444 },
4545 {
@@ -53,49 +53,49 @@ export const glossary = [
5353 name: "zapgar",
5454 symbol: "!>",
5555 usage: "wild",
56− slug: "/docs/hoon/reference/rune/zap/#zapgar",
56+ slug: "/docs/hoon/reference/rune/zap/#-zapgar",
5757 desc: "<code>[%zpgr p=hoon]</code>: wrap a noun in its type.",
5858 },
5959 {
6060 name: "zapgal",
6161 symbol: "!<",
6262 usage: "wild",
63− slug: "/docs/hoon/reference/rune/zap/#zapgal",
63+ slug: "/docs/hoon/reference/rune/zap/#-zapgal",
6464 desc: "<code>[%zpld p=spec q=hoon]</code>: extracts typed value from a vase.",
6565 },
6666 {
6767 name: "zapcol",
6868 symbol: "!:",
6969 usage: "wild",
70− slug: "/docs/hoon/reference/rune/zap/#zapcol",
70+ slug: "/docs/hoon/reference/rune/zap/#-zapcol",
7171 desc: "<code>[%dbug p=hoon]</code>: turn on stack trace",
7272 },
7373 {
7474 name: "zapdot",
7575 symbol: "!.",
7676 usage: "wild",
77− slug: "/docs/hoon/reference/rune/zap/#zapdot",
77+ slug: "/docs/hoon/reference/rune/zap/#-zapdot",
7878 desc: "Turn off stack trace for a subexpression <code>p</code>",
7979 },
8080 {
8181 name: "zaptis",
8282 symbol: "!=",
8383 usage: "wild",
84− slug: "/docs/hoon/reference/rune/zap/#zaptis",
84+ slug: "/docs/hoon/reference/rune/zap/#-zaptis",
8585 desc: "<code>[%zpts p=hoon]</code>: make the Nock formula for a Hoon expression.",
8686 },
8787 {
8888 name: "zapwut",
8989 symbol: "!?",
9090 usage: "wild",
91− slug: "/docs/hoon/reference/rune/zap/#zapwut",
91+ slug: "/docs/hoon/reference/rune/zap/#-zapwut",
9292 desc: "<code>[%zpwt p=@ q=hoon]</code>: restrict Hoon version.",
9393 },
9494 {
9595 name: "zapzap",
9696 symbol: "!!",
9797 usage: "wild",
98− slug: "/docs/hoon/reference/rune/zap/#zapzap",
98+ slug: "/docs/hoon/reference/rune/zap/#-zapzap",
9999 desc: "<code>[%zpzp ~]</code>: crash.",
100100 },
101101 {
@@ -109,84 +109,84 @@ export const glossary = [
109109 name: "tisgar",
110110 symbol: "=>",
111111 usage: "Subject Modification",
112− slug: "/docs/hoon/reference/rune/tis/#tisgar",
112+ slug: "/docs/hoon/reference/rune/tis/#-tisgar",
113113 desc: "<code>[%tsgr p=hoon q=hoon]</code>: compose two expressions.",
114114 },
115115 {
116116 name: "tisbar",
117117 symbol: "=|",
118118 usage: "Subject Modification",
119− slug: "/docs/hoon/reference/rune/tis/#tisbar",
119+ slug: "/docs/hoon/reference/rune/tis/#-tisbar",
120120 desc: "<code>[%tsbr p=spec q=hoon]</code>: combine a default type value with the subject.",
121121 },
122122 {
123123 name: "tiscol",
124124 symbol: "=:",
125125 usage: "Subject Modification",
126− slug: "/docs/hoon/reference/rune/tis/#tiscol",
126+ slug: "/docs/hoon/reference/rune/tis/#-tiscol",
127127 desc: "<code>[%tscl p=(list (pair wing hoon)) q=hoon]</code>: change multiple legs in the subject.",
128128 },
129129 {
130130 name: "tiscom",
131131 symbol: "=,",
132132 usage: "Subject Modification",
133− slug: "/docs/hoon/reference/rune/tis/#tiscom",
133+ slug: "/docs/hoon/reference/rune/tis/#-tiscom",
134134 desc: "<code>[%tscm p=hoon q=hoon]</code>: expose namespace",
135135 },
136136 {
137137 name: "tisdot",
138138 symbol: "=.",
139139 usage: "Subject Modification",
140− slug: "/docs/hoon/reference/rune/tis/#tisdot",
140+ slug: "/docs/hoon/reference/rune/tis/#-tisdot",
141141 desc: "<code>[%tsdt p=wing q=hoon r=hoon]</code>: change one leg in the subject.",
142142 },
143143 {
144144 name: "tishep",
145145 symbol: "=-",
146146 usage: "Subject Modification",
147− slug: "/docs/hoon/reference/rune/tis/#tishep",
147+ slug: "/docs/hoon/reference/rune/tis/#--tishep",
148148 desc: "<code>[%tshp p=hoon q=hoon]</code>: combine a new noun with the subject, inverted.",
149149 },
150150 {
151151 name: "tisket",
152152 symbol: "=^",
153153 usage: "Subject Modification",
154− slug: "/docs/hoon/reference/rune/tis/#tisket",
154+ slug: "/docs/hoon/reference/rune/tis/#-tisket",
155155 desc: "<code>[%tskt p=skin q=wing r=hoon s=hoon]</code>: pin the head of a pair; change",
156156 },
157157 {
158158 name: "tisgal",
159159 symbol: "=<",
160160 usage: "Subject Modification",
161− slug: "/docs/hoon/reference/rune/tis/#tisgal",
161+ slug: "/docs/hoon/reference/rune/tis/#-tisgal",
162162 desc: "<code>[%tsgl p=hoon q=hoon]</code>: compose two expressions, inverted.",
163163 },
164164 {
165165 name: "tislus",
166166 symbol: "=+",
167167 usage: "Subject Modification",
168− slug: "/docs/hoon/reference/rune/tis/#tislus",
168+ slug: "/docs/hoon/reference/rune/tis/#-tislus",
169169 desc: "<code>[%tsls p=hoon q=hoon]</code>: combine a new noun with the subject.",
170170 },
171171 {
172172 name: "tismic",
173173 symbol: "=;",
174174 usage: "Subject Modification",
175− slug: "/docs/hoon/reference/rune/tis/#tismic",
175+ slug: "/docs/hoon/reference/rune/tis/#-tismic",
176176 desc: "<code>[%tssm p=skin q=hoon r=hoon]</code>: combine a named noun with the subject, possibly with type annotation; inverted order.",
177177 },
178178 {
179179 name: "tisfas",
180180 symbol: "=/",
181181 usage: "Subject Modification",
182− slug: "/docs/hoon/reference/rune/tis/#tisfas",
182+ slug: "/docs/hoon/reference/rune/tis/#-tisfas",
183183 desc: "<code>[%tsfs p=skin q=hoon r=hoon]</code>: combine a named noun with the subject, possibly with type annotation.",
184184 },
185185 {
186186 name: "tissig",
187187 symbol: "=~",
188188 usage: "Subject Modification",
189− slug: "/docs/hoon/reference/rune/tis/#tissig",
189+ slug: "/docs/hoon/reference/rune/tis/#-tissig",
190190 desc: "<code>[%tssg p=(list hoon)]</code>: compose many expressions.",
191191 },
192192 {
@@ -200,7 +200,7 @@ export const glossary = [
200200 name: "tiswut",
201201 symbol: "=?",
202202 usage: "Subject Modification",
203− slug: "/docs/hoon/reference/rune/tis/#tiswut",
203+ slug: "/docs/hoon/reference/rune/tis/#-tiswut",
204204 desc: "<code>[$tswt p=wing q=hoon r=hoon s=hoon]</code>: conditionally change one leg in the subject.",
205205 },
206206 {
@@ -214,14 +214,14 @@ export const glossary = [
214214 name: "wutgar",
215215 symbol: "?>",
216216 usage: "Conditionals",
217− slug: "/docs/hoon/reference/rune/wut/#wutgar",
217+ slug: "/docs/hoon/reference/rune/wut/#-wutgar",
218218 desc: "<code>[%wtbn p=hoon q=hoon]</code>: positive assertion.",
219219 },
220220 {
221221 name: "wutbar",
222222 symbol: "?|",
223223 usage: "Conditionals",
224− slug: "/docs/hoon/reference/rune/wut/#wutbar",
224+ slug: "/docs/hoon/reference/rune/wut/#-wutbar",
225225 desc: "<code>[%wtbr p=(list hoon)]</code>: logical OR.",
226226 },
227227 {
@@ -235,70 +235,70 @@ export const glossary = [
235235 name: "wutdot",
236236 symbol: "?.",
237237 usage: "Conditionals",
238− slug: "/docs/hoon/reference/rune/wut/#wutdot",
238+ slug: "/docs/hoon/reference/rune/wut/#-wutdot",
239239 desc: "<code>[%wtdt p=hoon q=hoon r=hoon]</code>: branch on a boolean test, inverted.",
240240 },
241241 {
242242 name: "wuthep",
243243 symbol: "?-",
244244 usage: "Conditionals",
245− slug: "/docs/hoon/reference/rune/wut/#wuthep",
245+ slug: "/docs/hoon/reference/rune/wut/#--wuthep",
246246 desc: "<code>[%wthp p=wing q=(list (pair spec value))]</code>: switch against a union, with no default.",
247247 },
248248 {
249249 name: "wutket",
250250 symbol: "?^",
251251 usage: "Conditionals",
252− slug: "/docs/hoon/reference/rune/wut/#wutket",
252+ slug: "/docs/hoon/reference/rune/wut/#-wutket",
253253 desc: "<code>[%wtkt p=wing q=hoon r=hoon]</code>: branch on whether a wing",
254254 },
255255 {
256256 name: "wutgal",
257257 symbol: "?<",
258258 usage: "Conditionals",
259− slug: "/docs/hoon/reference/rune/wut/#wutgal",
259+ slug: "/docs/hoon/reference/rune/wut/#-wutgal",
260260 desc: "<code>[%wtgl p=hoon q=hoon]</code>: negative assertion.",
261261 },
262262 {
263263 name: "wutlus",
264264 symbol: "?+",
265265 usage: "Conditionals",
266− slug: "/docs/hoon/reference/rune/wut/#wutlus",
266+ slug: "/docs/hoon/reference/rune/wut/#-wutlus",
267267 desc: "<code>[%wtls p=wing q=hoon r=(list (pair spec hoon))]</code>: switch against",
268268 },
269269 {
270270 name: "wutpam",
271271 symbol: "?&",
272272 usage: "Conditionals",
273− slug: "/docs/hoon/reference/rune/wut/#wutpam",
273+ slug: "/docs/hoon/reference/rune/wut/#-wutpam",
274274 desc: "<code>[%wtpd p=(list hoon)]</code>: logical AND.",
275275 },
276276 {
277277 name: "wutsig",
278278 symbol: "?~",
279279 usage: "Conditionals",
280− slug: "/docs/hoon/reference/rune/wut/#wutsig",
280+ slug: "/docs/hoon/reference/rune/wut/#-wutsig",
281281 desc: "<code>[%wtsg p=wing q=hoon r=hoon]</code>: branch on whether a wing of the subject is null.",
282282 },
283283 {
284284 name: "wuttis",
285285 symbol: "?=",
286286 usage: "Conditionals",
287− slug: "/docs/hoon/reference/rune/wut/#wuttis",
287+ slug: "/docs/hoon/reference/rune/wut/#-wuttis",
288288 desc: "<code>[%wtts p=spec q=wing]</code>: test pattern match.",
289289 },
290290 {
291291 name: "wutpat",
292292 symbol: "?@",
293293 usage: "Conditionals",
294− slug: "/docs/hoon/reference/rune/wut/#wutpat",
294+ slug: "/docs/hoon/reference/rune/wut/#-wutpat",
295295 desc: "<code>[%wtpt p=wing q=hoon r=hoon]</code>: branch on whether a wing of the subject is an atom.",
296296 },
297297 {
298298 name: "wutzap",
299299 symbol: "?!",
300300 usage: "Conditionals",
301− slug: "/docs/hoon/reference/rune/wut/#wutzap",
301+ slug: "/docs/hoon/reference/rune/wut/#-wutzap",
302302 desc: "<code>[%wtzp p=hoon]</code>: logical NOT.",
303303 },
304304 {
@@ -312,84 +312,84 @@ export const glossary = [
312312 name: "barbuc",
313313 symbol: "|$",
314314 usage: "Cores",
315− slug: "/docs/hoon/reference/rune/bar/#barbuc",
315+ slug: "/docs/hoon/reference/rune/bar/#-barbuc",
316316 desc: "Declare a wet gate mold builder.",
317317 },
318318 {
319319 name: "barcab",
320320 symbol: "|_",
321321 usage: "Cores",
322− slug: "/docs/hoon/reference/rune/bar/#barcab",
322+ slug: "/docs/hoon/reference/rune/bar/#_-barcab",
323323 desc: "Produce a **door** (a core with a sample).",
324324 },
325325 {
326326 name: "barcen",
327327 symbol: "|%",
328328 usage: "Cores",
329− slug: "/docs/hoon/reference/rune/bar/#barcen",
329+ slug: "/docs/hoon/reference/rune/bar/#-barcen",
330330 desc: "Produce a core, <code>[battery payload]</code>.",
331331 },
332332 {
333333 name: "barcol",
334334 symbol: "|:",
335335 usage: "Cores",
336− slug: "/docs/hoon/reference/rune/bar/#barcol",
336+ slug: "/docs/hoon/reference/rune/bar/#-barcol",
337337 desc: "Produce a gate with a custom sample.",
338338 },
339339 {
340340 name: "bardot",
341341 symbol: "|.",
342342 usage: "Cores",
343− slug: "/docs/hoon/reference/rune/bar/#bardot",
343+ slug: "/docs/hoon/reference/rune/bar/#-bardot",
344344 desc: "Produce a trap (a core with one arm <code>$</code>).",
345345 },
346346 {
347347 name: "barhep",
348348 symbol: "|-",
349349 usage: "Cores",
350− slug: "/docs/hoon/reference/rune/bar/#barhep",
350+ slug: "/docs/hoon/reference/rune/bar/#--barhep",
351351 desc: "Produce a trap (a core with one arm <code>$</code>) and evaluate it.",
352352 },
353353 {
354354 name: "barket",
355355 symbol: "|^",
356356 usage: "Cores",
357− slug: "/docs/hoon/reference/rune/bar/#barket",
357+ slug: "/docs/hoon/reference/rune/bar/#-barket",
358358 desc: "Produce a core whose battery includes a <code>$</code> arm and compute the latter.",
359359 },
360360 {
361361 name: "barsig",
362362 symbol: "|~",
363363 usage: "Cores",
364− slug: "/docs/hoon/reference/rune/bar/#barsig",
364+ slug: "/docs/hoon/reference/rune/bar/#-barsig",
365365 desc: "Produce an iron gate.",
366366 },
367367 {
368368 name: "bartar",
369369 symbol: "|*",
370370 usage: "Cores",
371− slug: "/docs/hoon/reference/rune/bar/#bartar",
371+ slug: "/docs/hoon/reference/rune/bar/#-bartar",
372372 desc: "Produce a wet gate (one-armed core with sample).",
373373 },
374374 {
375375 name: "bartis",
376376 symbol: "|=",
377377 usage: "Cores",
378− slug: "/docs/hoon/reference/rune/bar/#bartis",
378+ slug: "/docs/hoon/reference/rune/bar/#-bartis",
379379 desc: "Produce a gate (a one-armed core with a sample).",
380380 },
381381 {
382382 name: "barpat",
383383 symbol: "|@",
384384 usage: "Cores",
385− slug: "/docs/hoon/reference/rune/bar/#barpat",
385+ slug: "/docs/hoon/reference/rune/bar/#-barpat",
386386 desc: "Produce a 'wet' core <code>[battery payload]</code>.",
387387 },
388388 {
389389 name: "barwut",
390390 symbol: "|?",
391391 usage: "Cores",
392− slug: "/docs/hoon/reference/rune/bar/#barwut",
392+ slug: "/docs/hoon/reference/rune/bar/#-barwut",
393393 desc: "Produce a lead trap.",
394394 },
395395 {
@@ -403,28 +403,28 @@ export const glossary = [
403403 name: "lusbar",
404404 symbol: "+|",
405405 usage: "Arms",
406− slug: "/docs/hoon/reference/rune/lus/#lusbar",
406+ slug: "/docs/hoon/reference/rune/lus/#-lusbar",
407407 desc: "Chapter label.",
408408 },
409409 {
410410 name: "lusbuc",
411411 symbol: "+$",
412412 usage: "Arms",
413− slug: "/docs/hoon/reference/rune/lus/#lusbuc",
413+ slug: "/docs/hoon/reference/rune/lus/#-lusbuc",
414414 desc: "Produce a structure arm (type definition).",
415415 },
416416 {
417417 name: "luslus",
418418 symbol: "++",
419419 usage: "Arms",
420− slug: "/docs/hoon/reference/rune/lus/#luslus",
420+ slug: "/docs/hoon/reference/rune/lus/#-luslus",
421421 desc: "Produce a normal arm.",
422422 },
423423 {
424424 name: "lustar",
425425 symbol: "+*",
426426 usage: "Arms",
427− slug: "/docs/hoon/reference/rune/lus/#lustar",
427+ slug: "/docs/hoon/reference/rune/lus/#-lustar",
428428 desc: "Produce a type constructor arm.",
429429 },
430430 {
@@ -438,49 +438,49 @@ export const glossary = [
438438 name: "colcab",
439439 symbol: ":_",
440440 usage: "Cells",
441− slug: "/docs/hoon/reference/rune/col/#colcab",
441+ slug: "/docs/hoon/reference/rune/col/#_-colcab",
442442 desc: "<code>[%clcb p=hoon q=hoon]</code>; construct a cell, inverted.",
443443 },
444444 {
445445 name: "colcol",
446446 symbol: "::",
447447 usage: "Cells",
448− slug: "/docs/hoon/reference/rune/col/#colcol",
448+ slug: "/docs/hoon/reference/rune/col/#-colcol",
449449 desc: "Code comment.",
450450 },
451451 {
452452 name: "colhep",
453453 symbol: ":-",
454454 usage: "Cells",
455− slug: "/docs/hoon/reference/rune/col/#colhep",
455+ slug: "/docs/hoon/reference/rune/col/#--colhep",
456456 desc: "<code>[%clhp p=hoon q=hoon]</code>: construct a cell (2-tuple).",
457457 },
458458 {
459459 name: "colket",
460460 symbol: ":^",
461461 usage: "Cells",
462− slug: "/docs/hoon/reference/rune/col/#colket",
462+ slug: "/docs/hoon/reference/rune/col/#-colket",
463463 desc: "<code>[%clkt p=hoon q=hoon r=hoon s=hoon]</code>: construct a quadruple (4-tuple).",
464464 },
465465 {
466466 name: "collus",
467467 symbol: ":+",
468468 usage: "Cells",
469− slug: "/docs/hoon/reference/rune/col/#collus",
469+ slug: "/docs/hoon/reference/rune/col/#-collus",
470470 desc: "<code>[%clls p=hoon q=hoon r=hoon]</code>: construct a triple (3-tuple).",
471471 },
472472 {
473473 name: "colsig",
474474 symbol: ":~",
475475 usage: "Cells",
476− slug: "/docs/hoon/reference/rune/col/#colsig",
476+ slug: "/docs/hoon/reference/rune/col/#-colsig",
477477 desc: "<code>[%clsg p=(list hoon)]</code>: construct a null-terminated list.",
478478 },
479479 {
480480 name: "coltar",
481481 symbol: ":*",
482482 usage: "Cells",
483− slug: "/docs/hoon/reference/rune/col/#coltar",
483+ slug: "/docs/hoon/reference/rune/col/#-coltar",
484484 desc: "<code>[%cltr p=(list hoon)]</code>: construct an n-tuple.",
485485 },
486486 {
@@ -494,21 +494,21 @@ export const glossary = [
494494 name: "cencab",
495495 symbol: "%_",
496496 usage: "Calls",
497− slug: "/docs/hoon/reference/rune/cen/#cencab",
497+ slug: "/docs/hoon/reference/rune/cen/#_-cencab",
498498 desc: "Resolve a wing with changes, preserving type.",
499499 },
500500 {
501501 name: "cencol",
502502 symbol: "%:",
503503 usage: "Calls",
504− slug: "/docs/hoon/reference/rune/cen/#cencol",
504+ slug: "/docs/hoon/reference/rune/cen/#-cencol",
505505 desc: "Call a gate with many arguments.",
506506 },
507507 {
508508 name: "cendot",
509509 symbol: "%.",
510510 usage: "Calls",
511− slug: "/docs/hoon/reference/rune/cen/#cendot",
511+ slug: "/docs/hoon/reference/rune/cen/#-cendot",
512512 desc: "Call a gate (function), inverted.",
513513 },
514514 {
@@ -522,35 +522,35 @@ export const glossary = [
522522 name: "cenket",
523523 symbol: "%^",
524524 usage: "Calls",
525− slug: "/docs/hoon/reference/rune/cen/#cenket",
525+ slug: "/docs/hoon/reference/rune/cen/#-cenket",
526526 desc: "Call gate with triple sample.",
527527 },
528528 {
529529 name: "cenlus",
530530 symbol: "%+",
531531 usage: "Calls",
532− slug: "/docs/hoon/reference/rune/cen/#cenlus",
532+ slug: "/docs/hoon/reference/rune/cen/#-cenlus",
533533 desc: "Call gate with a cell sample.",
534534 },
535535 {
536536 name: "censig",
537537 symbol: "%~",
538538 usage: "Calls",
539− slug: "/docs/hoon/reference/rune/cen/#censig",
539+ slug: "/docs/hoon/reference/rune/cen/#-censig",
540540 desc: "Evaluate an arm in a door.",
541541 },
542542 {
543543 name: "centar",
544544 symbol: "%*",
545545 usage: "Calls",
546− slug: "/docs/hoon/reference/rune/cen/#centar",
546+ slug: "/docs/hoon/reference/rune/cen/#-centar",
547547 desc: "Evaluate an expression, then resolve a wing with changes.",
548548 },
549549 {
550550 name: "centis",
551551 symbol: "%=",
552552 usage: "Calls",
553− slug: "/docs/hoon/reference/rune/cen/#centis",
553+ slug: "/docs/hoon/reference/rune/cen/#-centis",
554554 desc: "Resolve a wing with changes.",
555555 },
556556 {
@@ -564,70 +564,70 @@ export const glossary = [
564564 name: "ketbar",
565565 symbol: "^|",
566566 usage: "Casts",
567− slug: "/docs/hoon/reference/rune/ket/#ketbar",
567+ slug: "/docs/hoon/reference/rune/ket/#-ketbar",
568568 desc: "<code>[%ktbr p=hoon]</code>: convert a gold core to an iron core (contravariant).",
569569 },
570570 {
571571 name: "ketcol",
572572 symbol: "^:",
573573 usage: "Casts",
574− slug: "/docs/hoon/reference/rune/ket/#ketcol",
574+ slug: "/docs/hoon/reference/rune/ket/#-ketcol",
575575 desc: "<code>[%ktcl p=spec]</code>: 'factory' gate for type <code>p</code>.",
576576 },
577577 {
578578 name: "ketdot",
579579 symbol: "^.",
580580 usage: "Casts",
581− slug: "/docs/hoon/reference/rune/ket/#ketdot",
581+ slug: "/docs/hoon/reference/rune/ket/#-ketdot",
582582 desc: "<code>[%ktdt p=hoon q=hoon]</code>: typecast on value produced by passing <code>q</code> to <code>p</code>.",
583583 },
584584 {
585585 name: "kethep",
586586 symbol: "^-",
587587 usage: "Casts",
588− slug: "/docs/hoon/reference/rune/ket/#kethep",
588+ slug: "/docs/hoon/reference/rune/ket/#--kethep",
589589 desc: "<code>[%kthp p=spec q=hoon]</code>: typecast by explicit type label.",
590590 },
591591 {
592592 name: "ketlus",
593593 symbol: "^+",
594594 usage: "Casts",
595− slug: "/docs/hoon/reference/rune/ket/#ketlus",
595+ slug: "/docs/hoon/reference/rune/ket/#-ketlus",
596596 desc: "<code>[%ktls p=hoon q=hoon]</code>: typecast by inferred type.",
597597 },
598598 {
599599 name: "ketpam",
600600 symbol: "^&",
601601 usage: "Casts",
602− slug: "/docs/hoon/reference/rune/ket/#ketpam",
602+ slug: "/docs/hoon/reference/rune/ket/#-ketpam",
603603 desc: "<code>[%ktpd p=hoon]</code>: convert a core to a zinc core (covariant).",
604604 },
605605 {
606606 name: "ketsig",
607607 symbol: "^~",
608608 usage: "Casts",
609− slug: "/docs/hoon/reference/rune/ket/#ketsig",
609+ slug: "/docs/hoon/reference/rune/ket/#-ketsig",
610610 desc: "<code>[%ktsg p=hoon]</code>: fold constant at compile time.",
611611 },
612612 {
613613 name: "kettar",
614614 symbol: "^*",
615615 usage: "Casts",
616− slug: "/docs/hoon/reference/rune/ket/#kettar",
616+ slug: "/docs/hoon/reference/rune/ket/#-kettar",
617617 desc: "<code>[%kttr p=spec]</code>: Produce example type value.",
618618 },
619619 {
620620 name: "kettis",
621621 symbol: "^=",
622622 usage: "Casts",
623− slug: "/docs/hoon/reference/rune/ket/#kettis",
623+ slug: "/docs/hoon/reference/rune/ket/#-kettis",
624624 desc: "<code>[%ktts p=skin q=hoon]</code>: Bind name to a value.",
625625 },
626626 {
627627 name: "ketwut",
628628 symbol: "^?",
629629 usage: "Casts",
630− slug: "/docs/hoon/reference/rune/ket/#ketwut",
630+ slug: "/docs/hoon/reference/rune/ket/#-ketwut",
631631 desc: "<code>[%ktwt p=hoon]</code>: convert any core to a lead core (bivariant).",
632632 },
633633 {
@@ -641,91 +641,91 @@ export const glossary = [
641641 name: "bucbar",
642642 symbol: "$|",
643643 usage: "Structures",
644− slug: "/docs/hoon/reference/rune/buc/#bucbar",
644+ slug: "/docs/hoon/reference/rune/buc/#-bucbar",
645645 desc: "<code>[%bsbr p=spec q=hoon]</code>: structure that satisfies a validator.",
646646 },
647647 {
648648 name: "buccab",
649649 symbol: "$_",
650650 usage: "Structures",
651− slug: "/docs/hoon/reference/rune/buc/#buccab",
651+ slug: "/docs/hoon/reference/rune/buc/#_-buccab",
652652 desc: "<code>[%bscb p=hoon]</code>: structure that normalizes to an example.",
653653 },
654654 {
655655 name: "buccen",
656656 symbol: "$%",
657657 usage: "Structures",
658− slug: "/docs/hoon/reference/rune/buc/#buccen",
658+ slug: "/docs/hoon/reference/rune/buc/#-buccen",
659659 desc: "<code>[%bscn p=(list spec)]</code>: structure which recognizes a union tagged by head atom.",
660660 },
661661 {
662662 name: "buccol",
663663 symbol: "$:",
664664 usage: "Structures",
665− slug: "/docs/hoon/reference/rune/buc/#buccol",
665+ slug: "/docs/hoon/reference/rune/buc/#-buccol",
666666 desc: "<code>[%bscl p=(list spec)]</code>: form a cell type.",
667667 },
668668 {
669669 name: "bucgal",
670670 symbol: "$<",
671671 usage: "Structures",
672− slug: "/docs/hoon/reference/rune/buc/#bucgal",
672+ slug: "/docs/hoon/reference/rune/buc/#-bucgal",
673673 desc: "<code>[%bsld p=spec q=spec]</code>: restrict a mold by excluding some given mold.",
674674 },
675675 {
676676 name: "bucgar",
677677 symbol: "$>",
678678 usage: "Structures",
679− slug: "/docs/hoon/reference/rune/buc/#bucgar",
679+ slug: "/docs/hoon/reference/rune/buc/#-bucgar",
680680 desc: "<code>[%bshp p=spec q=spec]</code>: filter a mold to obtain a new mold.",
681681 },
682682 {
683683 name: "buchep",
684684 symbol: "$-",
685685 usage: "Structures",
686− slug: "/docs/hoon/reference/rune/buc/#buchep",
686+ slug: "/docs/hoon/reference/rune/buc/#--buchep",
687687 desc: "<code>[%bshp p=spec q=spec]</code>: structure that normalizes to an example gate.",
688688 },
689689 {
690690 name: "bucket",
691691 symbol: "$^",
692692 usage: "Structures",
693− slug: "/docs/hoon/reference/rune/buc/#bucket",
693+ slug: "/docs/hoon/reference/rune/buc/#-bucket",
694694 desc: "<code>[%bskt p=spec q=spec]</code>: structure which normalizes a union tagged by head depth (cell).",
695695 },
696696 {
697697 name: "bucsig",
698698 symbol: "$~",
699699 usage: "Structures",
700− slug: "/docs/hoon/reference/rune/buc/#bucsig",
700+ slug: "/docs/hoon/reference/rune/buc/#-bucsig",
701701 desc: "<code>[%bssg p=hoon q=spec]</code>: define a custom type default value",
702702 },
703703 {
704704 name: "bucpam",
705705 symbol: "$&",
706706 usage: "Structures",
707− slug: "/docs/hoon/reference/rune/buc/#bucpam",
707+ slug: "/docs/hoon/reference/rune/buc/#-bucpam",
708708 desc: "<code>[%bspd p=spec q=hoon]</code>: repair a value of a tagged union type",
709709 },
710710 {
711711 name: "bucpat",
712712 symbol: "$@",
713713 usage: "Structures",
714− slug: "/docs/hoon/reference/rune/buc/#bucpat",
714+ slug: "/docs/hoon/reference/rune/buc/#-bucpat",
715715 desc: "<code>[%bsvt p=spec q=spec]</code>: structure which normalizes a union tagged by head depth (atom).",
716716 },
717717 {
718718 name: "buctis",
719719 symbol: "$=",
720720 usage: "Structures",
721− slug: "/docs/hoon/reference/rune/buc/#buctis",
721+ slug: "/docs/hoon/reference/rune/buc/#-buctis",
722722 desc: "<code>[%bsts p=skin q=spec]</code>: structure which wraps a face around another structure.",
723723 },
724724 {
725725 name: "bucwut",
726726 symbol: "$?",
727727 usage: "Structures",
728− slug: "/docs/hoon/reference/rune/buc/#bucwut",
728+ slug: "/docs/hoon/reference/rune/buc/#-bucwut",
729729 desc: "<code>[%bswt p=(list spec)]</code>: form a type from a union of other types.",
730730 },
731731 {
@@ -739,56 +739,56 @@ export const glossary = [
739739 name: "miccol",
740740 symbol: ";:",
741741 usage: "Make",
742− slug: "/docs/hoon/reference/rune/mic/#miccol",
742+ slug: "/docs/hoon/reference/rune/mic/#-miccol",
743743 desc: "<code>[%mccl p=hoon q=(list hoon)]</code>: call a binary function as an n-ary function.",
744744 },
745745 {
746746 name: "micgal",
747747 symbol: ";<",
748748 usage: "Make",
749− slug: "/docs/hoon/reference/rune/mic/#micgal",
749+ slug: "/docs/hoon/reference/rune/mic/#-micgal",
750750 desc: "<code>[%mcgl p=spec q=hoon r=hoon s=hoon]</code>: monadic do notation.",
751751 },
752752 {
753753 name: "miclus",
754754 symbol: ";+",
755755 usage: "Make",
756− slug: "/docs/hoon/reference/rune/mic/#miclus",
756+ slug: "/docs/hoon/reference/rune/mic/#-miclus",
757757 desc: "make a single XML node (Sail)",
758758 },
759759 {
760760 name: "micmic",
761761 symbol: ";;",
762762 usage: "Make",
763− slug: "/docs/hoon/reference/rune/mic/#micmic",
763+ slug: "/docs/hoon/reference/rune/mic/#-micmic",
764764 desc: "<code>[%mcmc p=spec q=hoon]</code>: normalize with a mold, asserting fixpoint.",
765765 },
766766 {
767767 name: "micfas",
768768 symbol: ";/",
769769 usage: "Make",
770− slug: "/docs/hoon/reference/rune/mic/#micfas",
770+ slug: "/docs/hoon/reference/rune/mic/#-micfas",
771771 desc: "<code>[%mcnt p=hoon]</code>: tape as XML element.",
772772 },
773773 {
774774 name: "micsig",
775775 symbol: ";~",
776776 usage: "Make",
777− slug: "/docs/hoon/reference/rune/mic/#micsig",
777+ slug: "/docs/hoon/reference/rune/mic/#-micsig",
778778 desc: "<code>[%mcsg p=hoon q=(list hoon)]</code>: glue a pipeline together with a",
779779 },
780780 {
781781 name: "mictar",
782782 symbol: ";*",
783783 usage: "Make",
784− slug: "/docs/hoon/reference/rune/mic/#mictar",
784+ slug: "/docs/hoon/reference/rune/mic/#-mictar",
785785 desc: "Make a list of XML nodes from complex Hoon expression (Sail).",
786786 },
787787 {
788788 name: "mictis",
789789 symbol: ";=",
790790 usage: "Make",
791− slug: "/docs/hoon/reference/rune/mic/#mictis",
791+ slug: "/docs/hoon/reference/rune/mic/#-mictis",
792792 desc: "Make a list of XML nodes (Sail).",
793793 },
794794 {
@@ -802,84 +802,84 @@ export const glossary = [
802802 name: "siggar",
803803 symbol: "~>",
804804 usage: "Hints",
805− slug: "/docs/hoon/reference/rune/sig/#siggar",
805+ slug: "/docs/hoon/reference/rune/sig/#-siggar",
806806 desc: "<code>[%sgbn p=$@(term [p=term q=hoon]) q=hoon]</code>: raw hint, applied",
807807 },
808808 {
809809 name: "sigbar",
810810 symbol: "~|",
811811 usage: "Hints",
812− slug: "/docs/hoon/reference/rune/sig/#sigbar",
812+ slug: "/docs/hoon/reference/rune/sig/#-sigbar",
813813 desc: "<code>[%sgbr p=hoon q=hoon]</code>: tracing printf.",
814814 },
815815 {
816816 name: "sigbuc",
817817 symbol: "~$",
818818 usage: "Hints",
819− slug: "/docs/hoon/reference/rune/sig/#sigbuc",
819+ slug: "/docs/hoon/reference/rune/sig/#-sigbuc",
820820 desc: "<code>[%sgbs p=term q=hoon]</code>: profiling hit counter.",
821821 },
822822 {
823823 name: "sigcab",
824824 symbol: "~_",
825825 usage: "Hints",
826− slug: "/docs/hoon/reference/rune/sig/#sigcab",
826+ slug: "/docs/hoon/reference/rune/sig/#_-sigcab",
827827 desc: "<code>[%sgcb p=hoon q=hoon]</code>: user-formatted tracing printf.",
828828 },
829829 {
830830 name: "sigcen",
831831 symbol: "~%",
832832 usage: "Hints",
833− slug: "/docs/hoon/reference/rune/sig/#sigcen",
833+ slug: "/docs/hoon/reference/rune/sig/#-sigcen",
834834 desc: "<code>[%sgcn p=term q=wing r=(list [term hoon]) s=hoon]</code>: jet registration.",
835835 },
836836 {
837837 name: "siggal",
838838 symbol: "~<",
839839 usage: "Hints",
840− slug: "/docs/hoon/reference/rune/sig/#siggal",
840+ slug: "/docs/hoon/reference/rune/sig/#-siggal",
841841 desc: "<code>[%sgld p=$@(term [p=term q=hoon]) q=hoon]</code>: raw hint, applied to product.",
842842 },
843843 {
844844 name: "siglus",
845845 symbol: "~+",
846846 usage: "Hints",
847− slug: "/docs/hoon/reference/rune/sig/#siglus",
847+ slug: "/docs/hoon/reference/rune/sig/#-siglus",
848848 desc: "<code>[%sgls p=hoon]</code>: cache a computation.",
849849 },
850850 {
851851 name: "sigfas",
852852 symbol: "~/",
853853 usage: "Hints",
854− slug: "/docs/hoon/reference/rune/sig/#sigfas",
854+ slug: "/docs/hoon/reference/rune/sig/#-sigfas",
855855 desc: "<code>[%sgnt p=term q=hoon]</code>: jet registration for gate with",
856856 },
857857 {
858858 name: "sigpam",
859859 symbol: "~&",
860860 usage: "Hints",
861− slug: "/docs/hoon/reference/rune/sig/#sigpam",
861+ slug: "/docs/hoon/reference/rune/sig/#-sigpam",
862862 desc: "<code>[%sgpd p=hoon q=hoon]</code>: debugging printf.",
863863 },
864864 {
865865 name: "sigtis",
866866 symbol: "~=",
867867 usage: "Hints",
868− slug: "/docs/hoon/reference/rune/sig/#sigtis",
868+ slug: "/docs/hoon/reference/rune/sig/#-sigtis",
869869 desc: "<code>[%sgts p=hoon q=hoon]</code>: detect duplicate.",
870870 },
871871 {
872872 name: "sigwut",
873873 symbol: "~?",
874874 usage: "Hints",
875− slug: "/docs/hoon/reference/rune/sig/#sigwut",
875+ slug: "/docs/hoon/reference/rune/sig/#-sigwut",
876876 desc: "<code>[%sgwt p=hoon q=hoon r=hoon]</code>: conditional debug printf.",
877877 },
878878 {
879879 name: "sigzap",
880880 symbol: "~!",
881881 usage: "Hints",
882− slug: "/docs/hoon/reference/rune/sig/#sigzap",
882+ slug: "/docs/hoon/reference/rune/sig/#-sigzap",
883883 desc: "<code>[%sgzp p=hoon q=hoon]</code>: print type on compilation fail.",
884884 },
885885 {
@@ -893,14 +893,14 @@ export const glossary = [
893893 name: "hephep",
894894 symbol: "--",
895895 usage: "Terminators",
896− slug: "/docs/hoon/reference/rune/terminators/#hephep",
896+ slug: "/docs/hoon/reference/rune/terminators/#---hephep",
897897 desc: "The <code>--</code> rune is used to indicate the end of a core expression.",
898898 },
899899 {
900900 name: "tistis",
901901 symbol: "==",
902902 usage: "Terminators",
903− slug: "/docs/hoon/reference/rune/terminators/#tistis",
903+ slug: "/docs/hoon/reference/rune/terminators/#-tistis",
904904 desc: "The <code>==</code> rune is used to indicate the end of a 'jogging' or 'running' series of Hoon expressions.",
905905 },
906906 {
lib/hooks.js+22
@@ -35,3 +35,25 @@ export function useCopyToClipboard(text, notifyTimeout = 1000) {
3535
3636 return [copyStatus, copy];
3737}
38+
39+function getStorageValue(key, defaultValue) {
40+ // getting stored value
41+ if (typeof window !== "undefined") {
42+ const saved = window.localStorage.getItem(key);
43+ const initial = saved !== null ? JSON.parse(saved) : defaultValue;
44+ return initial;
45+ }
46+}
47+
48+export const useLocalStorage = (key, defaultValue) => {
49+ const [value, setValue] = useState(() => {
50+ return getStorageValue(key, defaultValue);
51+ });
52+
53+ useEffect(() => {
54+ // storing input name
55+ window.localStorage.setItem(key, JSON.stringify(value));
56+ }, [key, value]);
57+
58+ return [value, setValue];
59+};
lib/lib.js+1−1
@@ -182,7 +182,7 @@ export function formatTimeZone(dateTimeObject) {
182182
183183// Formats a DateTime object to something like "Jul 15, 2021"
184184export function formatDate(dateTimeObject) {
185− return dateTimeObject.toLocaleString(DateTime.DATE_HUGE);
185+ return dateTimeObject.toLocaleString(DateTime.DATE_FULL);
186186}
187187
188188// export function generateCrumbs(path) {
next.config.js+1
@@ -1,4 +1,5 @@
11module.exports = {
2+ reactStrictMode: false,
23 // target: 'serverless',
34 webpack: (config, { isServer }) => {
45 // Fixes npm packages that depend on `fs` module
package-lock.json+5
@@ -5748,6 +5748,11 @@
57485748 "resolved": "https://registry.npmjs.org/react-swipeable/-/react-swipeable-6.1.2.tgz",
57495749 "integrity": "sha512-vfZtOZNivwd/aI+ZZH1Grx0eQBdbV1UI3pB9p65jbW5guHHdSIPpKsND6XmaiZXP5REOOc9Ckfr36ChswPqwsA=="
57505750 },
5751+ "react-use-localstorage": {
5752+ "version": "3.5.3",
5753+ "resolved": "https://registry.npmjs.org/react-use-localstorage/-/react-use-localstorage-3.5.3.tgz",
5754+ "integrity": "sha512-1oNvJmo72G4v5P9ytJZZTb6ywD3UzWBiainTtfbNlb+U08hc+SOD5HqgiLTKUF0MxGcIR9JSnZGmBttNLXaQYA=="
5755+ },
57515756 "readable-stream": {
57525757 "version": "2.3.7",
57535758 "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
package.json+2−1
@@ -24,6 +24,7 @@
2424 "fuzzy-search": "^3.2.1",
2525 "gray-matter": "^4.0.3",
2626 "html-entities": "^2.3.2",
27+ "luxon": "^2.0.2",
2728 "next": "10.2.3",
2829 "patch-package": "^6.4.7",
2930 "postcss": "^8.3.0",
@@ -31,6 +32,7 @@
3132 "react-dom": "17.0.2",
3233 "react-hotkeys": "^2.0.0",
3334 "react-swipeable": "^6.1.2",
35+ "react-use-localstorage": "^3.5.3",
3436 "remark": "^13.0.0",
3537 "remark-gfm": "^1.0.0",
3638 "remark-heading-id": "^1.0.0",
@@ -39,7 +41,6 @@
3941 "remark-slug": "^6.0.0",
4042 "tailwindcss": "^2.1.4",
4143 "toml": "^3.0.0",
42− "luxon": "^2.0.2",
4344 "unified": "^9.2.1"
4445 },
4546 "devDependencies": {
pages/blog/[slug].js+9−18
@@ -18,6 +18,7 @@ import SingleColumn from "../../components/SingleColumn";
1818import Section from "../../components/Section";
1919import Contact from "../../components/Contact";
2020import PostPreview from "../../components/PostPreview";
21+import TwoUp from "../../components/TwoUp";
2122import { decode } from "html-entities";
2223
2324export default function Post({
@@ -64,24 +65,14 @@ export default function Post({
6465 <Contact />
6566 </Section>
6667 <Section wide className="flex">
67− {previousPost === null ? (
68− <div className={"w-1/2 mr-4"} />
69− ) : (
70− <PostPreview
71− title="Previous Post"
72− post={previousPost}
73− className="mr-4 w-1/2"
74− />
75− )}
76− {nextPost === null ? (
77− <div className={"w-1/2 ml-4"} />
78− ) : (
79− <PostPreview
80− title="Next Post"
81− post={nextPost}
82− className="ml-4 w-1/2"
83− />
84− )}
68+ <TwoUp>
69+ {nextPost ? (
70+ <PostPreview title="Next Post" post={nextPost} />
71+ ) : null}
72+ {previousPost ? (
73+ <PostPreview title="Previous Post" post={previousPost} />
74+ ) : null}
75+ </TwoUp>
8576 </Section>
8677 </SingleColumn>
8778 <Footer />
pages/events.js+5−19
@@ -19,6 +19,8 @@ import {
1919 formatTime,
2020} from "../lib/lib";
2121
22+import { eventKeys } from "../lib/constants";
23+
2224export default function Events({ events, search }) {
2325 const post = {
2426 title: "Events",
@@ -71,7 +73,7 @@ export default function Events({ events, search }) {
7173 <h3 className="text-green-400">Happening Now</h3>
7274 </div>
7375 {happeningNow.map((post) => {
74− return <EventPreview big event={post} />;
76+ return <EventPreview big event={post} className="mb-8" />;
7577 })}
7678 </>
7779 ) : null}
@@ -81,7 +83,7 @@ export default function Events({ events, search }) {
8183 <h3 className="text-green-400">Coming Soon</h3>
8284 </div>
8385 {futureEvents.map((post) => {
84− return <EventPreview big event={post} />;
86+ return <EventPreview big event={post} className="mb-8" />;
8587 })}
8688 </>
8789 ) : null}
@@ -100,23 +102,7 @@ export default function Events({ events, search }) {
100102}
101103
102104export async function getStaticProps() {
103− const events = getAllEvents(
104− [
105− "title",
106− "ends",
107− "location",
108− "image",
109− "registration_url",
110− "youtube",
111− "description",
112− "starts",
113− "hosts",
114− "guests",
115− "dark",
116− "slug",
117− ],
118− "events"
119− );
105+ const events = getAllEvents(eventKeys, "events");
120106
121107 return {
122108 props: { events },
pages/events/[slug].js+37−90
@@ -21,8 +21,16 @@ import SingleColumn from "../../components/SingleColumn";
2121import Contact from "../../components/Contact";
2222import EventPreview from "../../components/EventPreview";
2323import Section from "../../components/Section";
24−import { Person, ReadableList, ShowOrHide } from "../../components/Snippets";
24+import TwoUp from "../../components/TwoUp";
25+
26+import {
27+ Person,
28+ ReadableList,
29+ ShowOrHide,
30+ DateRange,
31+} from "../../components/Snippets";
2532import { decode } from "html-entities";
33+import { eventKeys } from "../../lib/constants";
2634
2735export default function Event({
2836 event,
@@ -50,17 +58,11 @@ export default function Event({
5058 <Section narrow short>
5159 <h1>{event.title}</h1>
5260 <h3 className="mt-6">{event.description}</h3>
53− <p className="text-wall-600 mt-6">{formatDate(starts)}</p>
54− <p className="text-wall-600">
55− {formatTime(starts)}
56− <ShowOrHide condition={typeof event.ends !== "undefined"}>
57− {` to ${formatTime(ends)}`}
58− {" " + formatTimeZone(ends)}
59− </ShowOrHide>
60− </p>
61− <div className="mt-6">
61+ <p className="mt-6">{event.location}</p>
62+ <DateRange starts={starts} ends={ends} className="text-wall-400" />
63+ <div>
6264 <ShowOrHide condition={event.hosts}>
63− <p>
65+ <p className="mt-6">
6466 {"Hosted by "}
6567 <ReadableList>
6668 {event.hosts?.map((host, index) => {
@@ -103,8 +105,9 @@ export default function Event({
103105 </div>
104106 ) : null}
105107 </Section>
106− <Section short wide>
107− {event.youtube ? (
108+
109+ {event.youtube ? (
110+ <Section short wide>
108111 <iframe
109112 className="rounded-xl"
110113 width="100%"
@@ -114,8 +117,9 @@ export default function Event({
114117 allow="encrypted-media"
115118 allowFullScreen
116119 ></iframe>
117− ) : null}
118− </Section>
120+ </Section>
121+ ) : null}
122+
119123 <Section short narrow className="markdown">
120124 <article
121125 className="pt-12 w-full"
@@ -126,22 +130,21 @@ export default function Event({
126130 <Contact />
127131 </Section>
128132 <Section wide className="flex">
129− {previousEvent === null ? (
130− <div className={"w-1/2 mr-4"} />
131− ) : (
132− <div className="mr-4 w-1/2">
133− <h3 className="mb-2">Next Event</h3>
134− <EventPreview event={previousEvent} />
135− </div>
136− )}
137− {nextEvent === null ? (
138− <div className={"w-1/2 ml-4"} />
139− ) : (
140− <div className="mr-4 w-1/2">
141− <h3 className="mb-2">Previous Event</h3>
142− <EventPreview event={nextEvent} />
143− </div>
144− )}
133+ <TwoUp>
134+ {previousEvent ? (
135+ <div>
136+ <h3 className="mb-2">Next Event</h3>
137+ <EventPreview event={previousEvent} />
138+ </div>
139+ ) : null}
140+
141+ {nextEvent ? (
142+ <div>
143+ <h3 className="mb-2">Previous Event</h3>
144+ <EventPreview event={nextEvent} />
145+ </div>
146+ ) : null}
147+ </TwoUp>
145148 </Section>
146149 </SingleColumn>
147150 <Footer />
@@ -151,68 +154,12 @@ export default function Event({
151154
152155//
153156export async function getStaticProps({ params }) {
154− const nextEvent =
155− getNextPost(
156− params.slug,
157− [
158− "title",
159− "slug",
160− "ends",
161− "location",
162− "image",
163− "registration_url",
164− "youtube",
165− "description",
166− "starts",
167− "hosts",
168− "guests",
169− "dark",
170− "timezone",
171− ],
172− "events"
173− ) || null;
157+ const nextEvent = getNextPost(params.slug, eventKeys, "events") || null;
174158
175159 const previousEvent =
176− getPreviousPost(
177− params.slug,
178− [
179− "title",
180− "slug",
181− "ends",
182− "location",
183− "image",
184− "registration_url",
185− "youtube",
186− "description",
187− "starts",
188− "hosts",
189− "guests",
190− "dark",
191− "timezone",
192− ],
193− "events"
194− ) || null;
160+ getPreviousPost(params.slug, eventKeys, "events") || null;
195161
196− const event = getPostBySlug(
197− params.slug,
198− [
199− "title",
200− "ends",
201− "location",
202− "image",
203− "registration_url",
204− "youtube",
205− "description",
206− "starts",
207− "hosts",
208− "slug",
209− "guests",
210− "content",
211− "dark",
212− "timezone",
213− ],
214− "events"
215− );
162+ const event = getPostBySlug(params.slug, eventKeys, "events");
216163
217164 const markdown = await Markdown({ post: event });
218165
pages/grants.js+10−3
@@ -106,12 +106,19 @@ export default function Grants({
106106 activeTags.includes(category)
107107 );
108108
109+ const notCanceled = !post.extra.canceled;
110+
109111 const noTagsSelected = activeTags.length === 0;
110112 const hasType = post.taxonomies.grant_type.some((type) =>
111113 activeTypes.includes(type)
112114 );
113115
114− return (hasCategory || noTagsSelected) && byStatus(post) && hasType;
116+ return (
117+ (hasCategory || noTagsSelected) &&
118+ byStatus(post) &&
119+ hasType &&
120+ notCanceled
121+ );
115122 });
116123
117124 const allCount = postsByStatus.length;
@@ -262,13 +269,13 @@ export default function Grants({
262269 post={giftPosts[0]}
263270 className={`w-full md:w-1/2 pr-0 pb-8 md:pr-4`}
264271 key={giftPosts[0].slug}
265− section={giftPosts[0].section}
272+ section="updates"
266273 />
267274 <PostPreview
268275 post={giftPosts[1]}
269276 className={`w-full md:w-1/2 pl-0 pb-8 md:pl-4`}
270277 key={giftPosts[1].slug}
271− section={giftPosts[1].section}
278+ section="blog"
272279 />
273280 </div>
274281 </div>
pages/grants/[slug].js+2−1
@@ -17,6 +17,7 @@ import Footer from "../../components/Footer";
1717import SingleColumn from "../../components/SingleColumn";
1818import GrantPreview from "../../components/GrantPreview";
1919import Section from "../../components/Section";
20+import { DateTime } from "luxon";
2021
2122export default function Grant({ post, markdown, search, similarGrants }) {
2223 const router = useRouter();
@@ -47,7 +48,7 @@ export default function Grant({ post, markdown, search, similarGrants }) {
4748 </div>
4849 ) : null}
4950 <div className="type-ui text-wall-500 mt-4 md:mt-8 lg:mt-10">
50− {formatDate(new Date(post.date))}
51+ {formatDate(DateTime.fromISO(post.date))}
5152 </div>
5253 <div className="flex items-center flex-wrap mt-4 md:mt-8 lg:mt-10">
5354 {post.taxonomies.grant_type.map((category) => {
pages/index.js+66−37
@@ -1,29 +1,58 @@
11import Head from "next/head";
22import Link from "next/link";
33import React, { useState, useEffect } from "react";
4+
45import Container from "../components/Container";
56import Section from "../components/Section";
67import SingleColumn from "../components/SingleColumn";
78import Header from "../components/Header";
89import Footer from "../components/Footer";
910import BackgroundImage from "../components/BackgroundImage";
10−import TabCarousel from "../components/TabCarousel";
1111import Contact from "../components/Contact";
1212import PostPreview from "../components/PostPreview";
1313import EventPreview from "../components/EventPreview";
14+import Cross from "../components/icons/Cross";
15+import TwoUp from "../components/TwoUp";
1416import {
1517 getAllPosts,
1618 getAllEvents,
1719 formatDate,
1820 getOpenGrantsCount,
1921} from "../lib/lib";
20−import { contact } from "../lib/constants";
22+import { contact, eventKeys } from "../lib/constants";
23+import { useLocalStorage } from "../lib/hooks";
24+
25+const Banner = ({ children, isOpen, href, dismiss }) => {
26+ return (
27+ <div className="w-full flex justify-center bg-green-100">
28+ <SingleColumn>
29+ <div className="w-full layout">
30+ <div className="w-full flex justify-between items-center px-4 md:px-8 py-4">
31+ <a href={href} target="_blank">
32+ {children}
33+ </a>
34+ <button
35+ className="type-ui w-6 h-6 bg-green-400 flex items-center justify-center rounded-full text-white hover:opacity-70"
36+ onClick={(e) => {
37+ e.stopPropagation();
38+ dismiss();
39+ }}
40+ >
41+ <Cross width="10" height="10" fill="#E5F7F1" />
42+ </button>
43+ </div>
44+ </div>
45+ </SingleColumn>
46+ </div>
47+ );
48+};
2149
2250export default function Home({ posts, events, openGrantsCount, search }) {
23− const [tab, setTab] = useState(0);
2451 const [heroButton, setHeroButton] = useState(<div />);
52+ const [bannerElement, setBannerElement] = useState(null);
53+ const [isBannerOpen, setBanner] = useLocalStorage("isBannerOpen", true);
2554
26− const detectOS = () => {
55+ const selectDownloadButton = () => {
2756 const agent = window.navigator.appVersion;
2857 if (agent.includes("Win")) {
2958 return (
@@ -49,17 +78,37 @@ export default function Home({ posts, events, openGrantsCount, search }) {
4978 };
5079
5180 useEffect(() => {
52− setHeroButton(detectOS());
81+ setHeroButton(selectDownloadButton());
5382 }, []);
5483
84+ // Use this pattern when depending on client-side state or conditions like localStorage, user OS or geolocation.
85+ const selectBanner = (isBannerOpen) => {
86+ if (isBannerOpen) {
87+ return (
88+ <Banner
89+ href="http://assembly.urbit.org/"
90+ dismiss={() => setBanner(false)}
91+ >
92+ <p className="text-green-400 font-semibold hover:opacity-70">{`-> Join us October 15-17 for Assembly`}</p>
93+ </Banner>
94+ );
95+ } else {
96+ return null;
97+ }
98+ };
99+
100+ useEffect(() => {
101+ setBannerElement(selectBanner(isBannerOpen));
102+ }, [isBannerOpen]);
103+
55104 return (
56105 <Container>
57106 <Head>
58107 <title>urbit.org</title>
59108 </Head>
109+ {bannerElement}
60110 <SingleColumn>
61111 <Header search={search} />
62−
63112 {
64113 // Hero Statement
65114 }
@@ -197,14 +246,11 @@ export default function Home({ posts, events, openGrantsCount, search }) {
197246 <h2>Blog</h2>
198247 </div>
199248
200− <div className="flex flex-wrap">
201− <div className="w-full md:w-1/2 pr-0 pb-8 md:pr-4">
202− <PostPreview post={posts[0]} key={posts[0].slug} />
203− </div>
204− <div className="w-full md:w-1/2 pl-0 pb-8 md:pl-4">
205− <PostPreview post={posts[1]} key={posts[1].slug} />
206− </div>
207− </div>
249+ <TwoUp>
250+ <PostPreview post={posts[0]} key={posts[0].slug} />
251+ <PostPreview post={posts[1]} key={posts[1].slug} />
252+ </TwoUp>
253+
208254 <Link href="/blog">
209255 <button className="button-lg type-ui text-white bg-green-400">
210256 See More
@@ -220,14 +266,11 @@ export default function Home({ posts, events, openGrantsCount, search }) {
220266 <h2>Events</h2>
221267 </div>
222268
223− <div className="flex flex-wrap">
224− <div className="w-full md:w-1/2 pr-0 pb-8 md:pr-4">
225− <EventPreview event={events[0]} key={events[0].slug} />
226− </div>
227− <div className="w-full md:w-1/2 pl-0 pb-8 md:pl-4">
228− <EventPreview event={events[1]} key={events[1].slug} />
229− </div>
230− </div>
269+ <TwoUp>
270+ <EventPreview event={events[0]} key={events[0].slug} />
271+ <EventPreview event={events[1]} key={events[1].slug} />
272+ </TwoUp>
273+
231274 <Link href="/events">
232275 <button className="button-lg type-ui text-white bg-wall-600">
233276 More Events
@@ -256,21 +299,7 @@ export async function getStaticProps() {
256299 "blog"
257300 );
258301
259− const events = getAllEvents(
260− [
261− "title",
262− "slug",
263− "starts",
264− "guests",
265− "hosts",
266− "registration_url",
267− "ends",
268− "timezone",
269− "youtube",
270− "image",
271− ],
272− "events"
273− );
302+ const events = getAllEvents(eventKeys, "events");
274303
275304 return {
276305 props: { posts, events, openGrantsCount },
styles/globals.css+22−25
@@ -1,5 +1,4 @@
1−@import url("https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;500;600;700&display=swap");
2−@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Source+Code+Pro:wght@400;500;600;700&display=swap");
1+@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Source+Code+Pro:wght@400;500;600;700;800&display=swap");
32
43:root {
54 --tray-time: 0ms;
@@ -9,69 +8,67 @@ html,
98body {
109 padding: 0;
1110 margin: 0;
11+ /* Make all text wall-600 by default */
1212 @apply text-wall-600;
1313}
1414
15+/* Reset a tags */
1516a {
1617 color: inherit;
1718 text-decoration: none;
1819 cursor: pointer;
1920}
2021
21−b,
22−strong {
23− @apply font-semibold;
24−}
25−
22+/* Links in p tags should be green */
2623p a {
2724 @apply text-green-400 font-semibold;
2825}
2926
30−button {
31− border: none;
27+/* Reduce b and strong tag weight default */
28+b,
29+strong {
30+ @apply font-semibold;
3231}
3332
3433/* Typography */
3534
3635h1,
3736.type-h1 {
38− @apply text-3xl md:text-4xl lg:text-5xl font-bold leading-tight text-wall-600;
37+ @apply text-4xl lg:text-5xl font-extrabold leading-tight text-wall-600;
3938}
4039
4140h2,
4241.type-h2 {
43− @apply text-2xl md:text-3xl lg:text-4xl font-bold leading-tight;
42+ @apply text-3xl lg:text-4xl font-bold leading-tight;
4443}
4544
4645h3,
4746.type-h3 {
48− @apply text-lg md:text-xl lg:text-2xl font-semibold;
47+ @apply text-xl font-semibold;
4948}
5049
5150h4,
5251.type-ui {
53− @apply text-sm md:text-base lg:text-lg font-semibold;
52+ @apply text-lg font-semibold;
5453}
5554
56−.type-ui-small {
57− @apply text-xs lg:text-base font-medium;
55+h5,
56+.type-h5,
57+.type-bold {
58+ @apply text-base font-semibold leading-relaxed;
5859}
5960
6061p,
6162.type-p {
62− @apply text-sm md:text-base lg:text-base leading-relaxed;
63−}
64−
65−.type-bold {
66− @apply text-sm md:text-base lg:text-base font-semibold leading-relaxed;
63+ @apply text-base leading-relaxed;
6764}
6865
6966.type-sub {
70− @apply text-xs md:text-sm;
67+ @apply text-sm;
7168}
7269
7370.type-sub-bold {
74− @apply text-xs md:text-sm font-semibold;
71+ @apply text-sm font-semibold;
7572}
7673
7774code .type-code {
@@ -109,15 +106,15 @@ code .type-code {
109106}
110107
111108.button-lg {
112− @apply flex-center px-4 lg:px-6 py-1 rounded-xl h-10 md:h-14 type-ui;
109+ @apply flex-center px-6 py-1 rounded-xl h-14 type-ui;
113110}
114111
115112.badge-sm {
116− @apply flex-center type-ui-small px-4 lg:px-4 h-8 md:h-8 rounded-full;
113+ @apply flex-center text-base font-medium px-3 h-8 rounded-full;
117114}
118115
119116.badge-lg {
120− @apply flex-center type-ui px-4 lg:px-8 h-10 md:h-12 rounded-full;
117+ @apply flex-center type-ui px-5 h-12 rounded-full;
121118}
122119
123120.button-sm:hover,
styles/markdown.css+2−2
@@ -36,11 +36,11 @@
3636}
3737
3838.markdown p {
39− @apply text-sm md:text-lg leading-relaxed;
39+ @apply text-base md:text-lg leading-relaxed;
4040}
4141
4242.markdown li {
43− @apply py-2 ml-6 text-lg;
43+ @apply py-2 ml-6 text-base md:text-lg;
4444}
4545
4646.markdown hr {