mirror of
https://github.com/urbit/shrub.git
synced 2024-12-13 16:03:36 +03:00
more sitework
This commit is contained in:
parent
be038f4c11
commit
438732a670
@ -2,6 +2,7 @@
|
||||
::
|
||||
::::
|
||||
::
|
||||
/= bod /^ manx /: /===/pub/src/doc/say/intro /psal/
|
||||
/= sty /^ @t /: /===/pub/fab/site/styles /css/
|
||||
::
|
||||
:::: ~tomsyt-balsen
|
||||
@ -14,18 +15,11 @@
|
||||
;body
|
||||
;div(class "content container")
|
||||
;div(class "page documentation")
|
||||
;+ bod
|
||||
;h1(class "page-title"): Documentation
|
||||
;p ; If you want to build a deep understanding of how Urbit works,
|
||||
; start with Nock. If you would prefer to just try stuff
|
||||
; out, start with Arvo.
|
||||
==
|
||||
;p ; This documentation is a work in progress. Feedback and corrections
|
||||
; are welcome. Pull requests are encouraged. The repo for this site
|
||||
; lives ;{a(href "https://github.com/urbit/urbit.github.io") "here"}.
|
||||
; We would love your help in making this reference more useful.
|
||||
==
|
||||
;p ; Arvo is still actively being changed and updated.
|
||||
; As Arvo development cools, more documentation will emerge.
|
||||
==
|
||||
;ul
|
||||
;li
|
||||
|
596
main/pub/fab/site/styles.css
Normal file
596
main/pub/fab/site/styles.css
Normal file
@ -0,0 +1,596 @@
|
||||
|
||||
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
@media (min-width: 38rem) {
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: #515151;
|
||||
background-color: #fff;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
/* No `:visited` state is required by default (browsers will use `a`) */
|
||||
a {
|
||||
line-height: 1.1rem;
|
||||
color: #555;
|
||||
text-decoration: none;
|
||||
}
|
||||
li a, p a {
|
||||
display: inline-block;
|
||||
border-bottom: 2px solid #ccc;
|
||||
}
|
||||
/* `:focus` is linked to `:hover` for basic accessibility */
|
||||
a:hover,
|
||||
a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
li a:hover, p a:hover,
|
||||
li a:hover, p a:focus {
|
||||
border-bottom: 2px solid #555;
|
||||
}
|
||||
|
||||
/* Headings */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-bottom: .5rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.25;
|
||||
color: #313131;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
h1 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
h2 {
|
||||
margin-top: 1rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
h3 {
|
||||
margin-top: 1.5rem;
|
||||
font-size: .9rem;
|
||||
}
|
||||
h4, h5, h6 {
|
||||
margin-top: .9rem;
|
||||
font-size: .9rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: .03rem;
|
||||
}
|
||||
|
||||
/* Body text */
|
||||
p {
|
||||
max-width: 42rem;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
strong {
|
||||
color: #303030;
|
||||
}
|
||||
|
||||
|
||||
/* Lists */
|
||||
ul, ol, dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
/* Misc */
|
||||
hr {
|
||||
position: relative;
|
||||
margin: 1.5rem 0;
|
||||
border: 0;
|
||||
border-top: 1px solid #eee;
|
||||
border-bottom: 1px solid #fff;
|
||||
}
|
||||
|
||||
abbr {
|
||||
font-size: 85%;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
abbr[title] {
|
||||
cursor: help;
|
||||
border-bottom: 1px dotted #e5e5e5;
|
||||
}
|
||||
|
||||
/* Code */
|
||||
code,
|
||||
pre, .codeblock {
|
||||
font-family: Menlo, Monaco, "Courier New", monospace;
|
||||
}
|
||||
code {
|
||||
padding: .25em .5em;
|
||||
font-size: 90%;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: none;
|
||||
}
|
||||
pre, .codeblock {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
padding: 1rem;
|
||||
font-size: .9rem;
|
||||
line-height: 1.4;
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
width: 42rem;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
pre code, .codeblock code {
|
||||
padding: 0;
|
||||
font-size: 90%;
|
||||
color: inherit;
|
||||
background-color: transparent;
|
||||
}
|
||||
.highlight {
|
||||
margin-bottom: 1rem;
|
||||
border-radius: none;
|
||||
}
|
||||
.highlight pre {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Quotes */
|
||||
blockquote {
|
||||
padding: .5rem 1rem;
|
||||
padding-left: 0;
|
||||
max-width: 32rem;
|
||||
margin: .8rem 0;
|
||||
color: #7a7a7a;
|
||||
}
|
||||
blockquote p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@media (min-width: 30rem) {
|
||||
blockquote {
|
||||
padding-right: 5rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
margin: 0 0 1rem;
|
||||
border-radius: none;
|
||||
min-width:100%;
|
||||
max-width:100%;
|
||||
height:auto;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
table {
|
||||
margin-bottom: 1rem;
|
||||
width: 100%;
|
||||
border: 1px solid #e5e5e5;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
td,
|
||||
th {
|
||||
padding: .25rem .5rem;
|
||||
border: 1px solid #e5e5e5;
|
||||
}
|
||||
tbody tr:nth-child(odd) td,
|
||||
tbody tr:nth-child(odd) th {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Custom type
|
||||
*
|
||||
* Extend paragraphs with `.lead` for larger introductory text.
|
||||
*/
|
||||
|
||||
.lead {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Messages
|
||||
*
|
||||
* Show alert messages to users. You may add it to single elements like a `<p>`,
|
||||
* or to a parent if there are multiple elements to show.
|
||||
*/
|
||||
|
||||
.message {
|
||||
margin-bottom: 1rem;
|
||||
padding: 1rem;
|
||||
color: #717171;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Container
|
||||
*
|
||||
* Center the page content.
|
||||
*/
|
||||
|
||||
.container {
|
||||
max-width: 42rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Masthead
|
||||
*
|
||||
* Super small header above the content for site name and short description.
|
||||
*/
|
||||
|
||||
.masthead {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
.masthead-title {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
color: #505050;
|
||||
}
|
||||
.masthead-title a {
|
||||
color: #505050;
|
||||
}
|
||||
.masthead-title small {
|
||||
font-size: 75%;
|
||||
font-weight: 400;
|
||||
color: #c0c0c0;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Posts and pages
|
||||
*
|
||||
* Each post is wrapped in `.post` and is used on default and post layouts. Each
|
||||
* page is wrapped in `.page` and is only used on the page layout.
|
||||
*/
|
||||
|
||||
.page,
|
||||
.subpage,
|
||||
.post {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 4em;
|
||||
}
|
||||
|
||||
/* Blog post or page title */
|
||||
.page-title,
|
||||
.post-title,
|
||||
.post-title a {
|
||||
color: #303030;
|
||||
}
|
||||
|
||||
/* Meta data line below post title */
|
||||
.post-date {
|
||||
display: block;
|
||||
margin-top: -.5rem;
|
||||
margin-bottom: 1rem;
|
||||
color: #9a9a9a;
|
||||
}
|
||||
|
||||
/* Related posts */
|
||||
.related {
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
.related-posts {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.related-posts h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
.related-posts li small {
|
||||
font-size: 75%;
|
||||
color: #999;
|
||||
}
|
||||
.related-posts li a:hover {
|
||||
color: #268bd2;
|
||||
text-decoration: none;
|
||||
}
|
||||
.related-posts li a:hover small {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Pagination
|
||||
*
|
||||
* Super lightweight (HTML-wise) blog pagination. `span`s are provide for when
|
||||
* there are no more previous or next posts to show.
|
||||
*/
|
||||
|
||||
.pagination {
|
||||
overflow: hidden; /* clearfix */
|
||||
margin-left: -1rem;
|
||||
margin-right: -1rem;
|
||||
font-family: "PT Sans", Helvetica, Arial, sans-serif;
|
||||
color: #ccc;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Pagination items can be `span`s or `a`s */
|
||||
.pagination-item {
|
||||
display: block;
|
||||
padding: 1rem;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
.pagination-item:first-child {
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
/* Only provide a hover state for linked pagination items */
|
||||
a.pagination-item:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
@media (min-width: 30rem) {
|
||||
.pagination {
|
||||
margin: 3rem 0;
|
||||
}
|
||||
.pagination-item {
|
||||
float: left;
|
||||
width: 50%;
|
||||
}
|
||||
.pagination-item:first-child {
|
||||
margin-bottom: 0;
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
.pagination-item:last-child {
|
||||
margin-left: -1px;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.hll { background-color: #ffffcc }
|
||||
|
||||
|
||||
.css .o,
|
||||
.css .o + .nt,
|
||||
.css .nt + .nt { color: #999; }
|
||||
|
||||
.toggle {
|
||||
display: none;
|
||||
}
|
||||
.subnav li.active .toggle, .toggle.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.setup .subnav li.active > a {
|
||||
border-color: #000;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.sidebar-about {
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
.sidebar-about h1 {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
display: inline-block
|
||||
}
|
||||
|
||||
.sidebar-about > a:focus,
|
||||
.sidebar-about > a:hover {
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebar-about h1.title {
|
||||
border-bottom: 3px solid transparent;
|
||||
}
|
||||
|
||||
.sidebar-about h1.logo {
|
||||
margin-top: .6rem;
|
||||
margin-right: .6rem;
|
||||
margin-left: -2rem;
|
||||
margin-bottom: 0;
|
||||
height: 1rem;
|
||||
float: left;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 500px) {
|
||||
.sidebar-about h1.logo {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
img.logo {
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
}
|
||||
|
||||
.sidebar-about p.lead {
|
||||
font-size: .8rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.sidebar-nav {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.sidebar-nav li > a {
|
||||
border: none;
|
||||
border-bottom: 3px solid #fff;
|
||||
}
|
||||
|
||||
.sidebar-about h1.title:focus,
|
||||
.sidebar-about h1.title:hover,
|
||||
.sidebar-nav li > a.active,
|
||||
.sidebar-nav li > a:focus,
|
||||
.sidebar-nav li > a:hover {
|
||||
text-decoration: none;
|
||||
border-bottom: 3px solid #555;
|
||||
}
|
||||
|
||||
.sidebar-about > a > img {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.sidebar-nav-item {
|
||||
display: inline-block;
|
||||
min-width: 13rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
.footer .sidebar-nav-item {
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
ul,ol {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style-type: upper-roman;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: circle;
|
||||
}
|
||||
|
||||
ul.chat {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
ul.chat li {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.doc-hoon h1, .doc-hoon h2 {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.documentation > ul {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.documentation ul > li {
|
||||
float: left;
|
||||
width: 13rem;
|
||||
}
|
||||
|
||||
code, .subnav > ul > li > ul {
|
||||
font-family: "Menlo", "Courier New", courier, monospace;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.subnav > ul > li > ul {
|
||||
font-size: .9rem;
|
||||
overflow: hidden;
|
||||
height: 0;
|
||||
margin-right: 2rem;
|
||||
}
|
||||
|
||||
.subnav > ul > li.active > ul {
|
||||
overflow: visible;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.subnav > ul > li .section {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.setup .subnav li > a.expand, .subnav > ul > li .expand {
|
||||
font-size: .6rem;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.setup .subnav li > a.expand:before, .subnav > ul > li .expand:before {
|
||||
content: "\25B6";
|
||||
font-size: .6rem;
|
||||
}
|
||||
|
||||
.setup .subnav li > a.expand:before {
|
||||
margin-right: .6rem;
|
||||
}
|
||||
|
||||
.setup .subnav li.active > a.expand:before, .subnav > ul > li.active .expand:before {
|
||||
content: "\25BC";
|
||||
font-size: .6rem;
|
||||
}
|
||||
|
||||
.subnav, .subnav ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0 0 .6rem 0;
|
||||
}
|
||||
|
||||
.subnav ul > li > ul > li {
|
||||
margin-bottom: .3rem;
|
||||
}
|
||||
|
||||
.subnav a.active {
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #555;
|
||||
}
|
||||
|
||||
.page.setup .subnav .toggle {
|
||||
margin: 1rem 0 2rem 1.6rem;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 54rem) {
|
||||
.doc-hoon .subnav.arms a{
|
||||
font-size: .8rem;
|
||||
}
|
||||
.doc-hoon .subnav.runes, .doc-hoon .subnav.arms {
|
||||
position: fixed;
|
||||
top: 8rem;
|
||||
left: 50%;
|
||||
margin-left: -30rem;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.subnav > ul > li > ul {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
18
main/pub/fab/site/tut/arvo3/hymn.hook
Normal file
18
main/pub/fab/site/tut/arvo3/hymn.hook
Normal file
@ -0,0 +1,18 @@
|
||||
/= sty /^ @t /: /===/pub/fab/site/styles /css/
|
||||
/= bod /^ manx /: /===/pub/src/doc/say/arvo/app /psal/
|
||||
::
|
||||
:::: ~tomsyt-balsen
|
||||
::
|
||||
;html
|
||||
;head
|
||||
;title: Urbit: Personal Cloud Computing
|
||||
;style:"{(trip sty)}"
|
||||
==
|
||||
;body
|
||||
;div(class "content container")
|
||||
;div.subpage
|
||||
;+ bod
|
||||
==
|
||||
==
|
||||
==
|
||||
==
|
18
main/pub/fab/site/tut/arvo4/hymn.hook
Normal file
18
main/pub/fab/site/tut/arvo4/hymn.hook
Normal file
@ -0,0 +1,18 @@
|
||||
/= sty /^ @t /: /===/pub/fab/site/styles /css/
|
||||
/= bod /^ manx /: /===/pub/src/doc/say/arvo/pub2 /psal/
|
||||
::
|
||||
:::: ~tomsyt-balsen
|
||||
::
|
||||
;html
|
||||
;head
|
||||
;title: Urbit: Personal Cloud Computing
|
||||
;style:"{(trip sty)}"
|
||||
==
|
||||
;body
|
||||
;div(class "content container")
|
||||
;div.subpage
|
||||
;+ bod
|
||||
==
|
||||
==
|
||||
==
|
||||
==
|
18
main/pub/fab/site/tut/arvo5/hymn.hook
Normal file
18
main/pub/fab/site/tut/arvo5/hymn.hook
Normal file
@ -0,0 +1,18 @@
|
||||
/= sty /^ @t /: /===/pub/fab/site/styles /css/
|
||||
/= bod /^ manx /: /===/pub/src/doc/say/arvo/pub3 /psal/
|
||||
::
|
||||
:::: ~tomsyt-balsen
|
||||
::
|
||||
;html
|
||||
;head
|
||||
;title: Urbit: Personal Cloud Computing
|
||||
;style:"{(trip sty)}"
|
||||
==
|
||||
;body
|
||||
;div(class "content container")
|
||||
;div.subpage
|
||||
;+ bod
|
||||
==
|
||||
==
|
||||
==
|
||||
==
|
18
main/pub/fab/site/tut/setup/hymn.hook
Normal file
18
main/pub/fab/site/tut/setup/hymn.hook
Normal file
@ -0,0 +1,18 @@
|
||||
/= bod /^ manx /: /===/pub/src/doc/say/setup /psal/
|
||||
/= sty /^ @t /: /===/pub/fab/site/styles /css/
|
||||
::
|
||||
:::: ~tomsyt-balsen
|
||||
::
|
||||
;html
|
||||
;head
|
||||
;title: Urbit: Personal Cloud Computing
|
||||
;style:"{(trip sty)}"
|
||||
==
|
||||
;body
|
||||
;div(class "content container")
|
||||
;div.subpage
|
||||
;+ bod
|
||||
==
|
||||
==
|
||||
==
|
||||
==
|
987
main/pub/src/doc/ref/vol4.md
Normal file
987
main/pub/src/doc/ref/vol4.md
Normal file
@ -0,0 +1,987 @@
|
||||
Ames
|
||||
====
|
||||
|
||||
Ames is our networking protocol.
|
||||
|
||||
data models
|
||||
-----------
|
||||
|
||||
###`++fort`, formal state
|
||||
|
||||
```
|
||||
++ fort :: formal state
|
||||
$: %0 :: version
|
||||
gad=duct :: client interface
|
||||
hop=@da :: network boot date
|
||||
ton=town :: security
|
||||
zac=(map ship corn) :: flows by server
|
||||
== ::
|
||||
```
|
||||
|
||||
This is the state of our vane. Anything that must be remembered between
|
||||
calls to ames must be stored in this state.
|
||||
|
||||
`%0` is the version of the ames state model itself. If the data model `++fort`
|
||||
changes, then this number needs to be incremented, and an adapter must be
|
||||
written to upgrade the old state into the new state. Note that this is the
|
||||
version number of the model itself, not the contents. When the data changes,
|
||||
there is of course no need to change this.
|
||||
|
||||
`gad` is a `duct` over which we send `%send` cards to unix. This card is
|
||||
initialized when unix sends a `%barn` card as vere starts up. Vere treats this
|
||||
duct specially -- don't send anything weird over it.
|
||||
|
||||
`hop` is the network boot date. This is set when the `%kick` card is sent by
|
||||
vere on start up.
|
||||
|
||||
`ton` is a `++town`, where we store all of our security/encryption state. Note
|
||||
that this is shared across all ships on a pier.
|
||||
|
||||
`zac` is a map of ships to `++corn`. This stores all the per-ship state. The
|
||||
keys to this map are the ships on the current pier.
|
||||
|
||||
###`++town`, all security state
|
||||
|
||||
```
|
||||
++ town :: all security state
|
||||
$: lit=@ud :: imperial modulus
|
||||
any=@ :: entropy
|
||||
urb=(map ship sufi) :: all keys and routes
|
||||
fak=? ::
|
||||
== ::
|
||||
```
|
||||
|
||||
This is the security state of our pier.
|
||||
|
||||
`lit` is unused.
|
||||
|
||||
`any` is 256 bits of entropy. This entropy is used and updated in exactly two
|
||||
places: when we send a `%junk` card, and when we generate a new symmetric key
|
||||
in `++griz:lax:as:go`. When it is updated, it is updated by a SHA-256 hash of
|
||||
the current time and the old value of the entropy.
|
||||
|
||||
`urb` is a map of ships to `++sufi`. This is where we store all the per-ship
|
||||
state for the pier. The keys to this map are the ships on the current pier.
|
||||
|
||||
`fak` is true if we are on a fake network. This disables certain security
|
||||
checks so that anyone may run a fake `~zod`. This is used only for development.
|
||||
To use, run vere with the `-F` option (and the `-I ~zod` option for a fake
|
||||
`~zod`).
|
||||
|
||||
###`++sufi`, domestic host
|
||||
|
||||
```
|
||||
++ sufi :: domestic host
|
||||
$: hoy=(list ship) :: hierarchy
|
||||
val=wund :: private keys
|
||||
law=will :: server will
|
||||
seh=(map hand ,[p=ship q=@da]) :: key cache
|
||||
hoc=(map ship dore) :: neighborhood
|
||||
== ::
|
||||
```
|
||||
|
||||
This is the security state of a domestic server.
|
||||
|
||||
`hoy` is a list of the ships directly above us in the hierarchy of ships. For
|
||||
example, for `~hoclur-bicrel`, this would be `~tasruc` and `~tug`. See
|
||||
`++sein`.
|
||||
|
||||
`val` is a list of our private keys.
|
||||
|
||||
`law` is our certificate, which is a list of the XXX
|
||||
|
||||
`seh`
|
||||
|
||||
`hoc` is a map of ships to `++dore`. The stores all the security informatoin
|
||||
about foreign ships. The keys to this map are the neighbors (ships we have
|
||||
been in contact with) of this domestic server.
|
||||
|
||||
###`++wund`, private keys
|
||||
|
||||
```
|
||||
++ wund (list ,[p=life q=ring r=acru]) :: mace in action
|
||||
```
|
||||
|
||||
This is a list of our own private keys, indexed by life. The key itself is
|
||||
the `++ring`, and the `++acru` is the encryption engine. We generate the
|
||||
`++acru` from the private key by calling `++weur`. Thus, we can at any time
|
||||
regenerate our `++wund` from a `++mace`. The current crypto is at the head of
|
||||
the list and can be accessed with
|
||||
`++sen:as:go`.
|
||||
|
||||
###`++ring`, private key
|
||||
|
||||
```
|
||||
++ ring ,@ :: private key
|
||||
```
|
||||
|
||||
This is a private key. The first byte is reserved to identify the type of
|
||||
cryptography. Lower-case means public key, upper-case means public key, and
|
||||
the letter identifies which `++acru` to use.
|
||||
|
||||
###`++pass`, public key
|
||||
|
||||
```
|
||||
++ pass ,@ :: public key
|
||||
```
|
||||
|
||||
This is a public key. The first byte is reserved to identify the type of
|
||||
cryptography. Lower-case means public key, upper-case means public key, and
|
||||
the letter identifies which `++acru` to use.
|
||||
|
||||
###`++mace`, private secrets
|
||||
|
||||
```
|
||||
++ mace (list ,[p=life q=ring]) :: private secrets
|
||||
```
|
||||
|
||||
This is a list of the our private keys, indexed by life. From this we can
|
||||
generate a `++wund` for actual use.
|
||||
|
||||
###`++skin`, encoding stem
|
||||
|
||||
```
|
||||
++ skin ?(%none %open %fast %full) :: encoding stem
|
||||
```
|
||||
|
||||
This defines the type of encryption used for each message. `%none` refers
|
||||
to messages sent in the clear, `%open` refers to signed messages, `%full`
|
||||
refers to sealed messages, and `%fast` refers to symmetrically encrypted
|
||||
messages. See `++acru` for details.
|
||||
|
||||
###`++acru`, asymmetric cryptosuite
|
||||
|
||||
```
|
||||
++ acru :: asym cryptosuite
|
||||
$_ ^? |% :: opaque object
|
||||
++ as ^? :: asym ops
|
||||
|% ++ seal |=([a=pass b=@ c=@] _@) :: encrypt to a
|
||||
++ sign |=([a=@ b=@] _@) :: certify as us
|
||||
++ sure |=([a=@ b=@] *(unit ,@)) :: authenticate from us
|
||||
++ tear |= [a=pass b=@] :: accept from a
|
||||
*(unit ,[p=@ q=@]) ::
|
||||
-- ::
|
||||
++ de |+([a=@ b=@] *(unit ,@)) :: symmetric de, soft
|
||||
++ dy |+([a=@ b=@] _@) :: symmetric de, hard
|
||||
++ en |+([a=@ b=@] _@) :: symmetric en
|
||||
++ ex ^? :: export
|
||||
|% ++ fig _@uvH :: fingerprint
|
||||
++ pac _@uvG :: default passcode
|
||||
++ pub *pass :: public key
|
||||
++ sec *ring :: private key
|
||||
--
|
||||
++ nu ^? :: reconstructors
|
||||
|% ++ pit |=([a=@ b=@] ^?(..nu)) :: from [width seed]
|
||||
++ nol |=(a=@ ^?(..nu)) :: from naked ring
|
||||
++ com |=(a=@ ^?(..nu)) :: from naked pass
|
||||
--
|
||||
--
|
||||
```
|
||||
|
||||
This is an opaque interface for a general asymmetric cryptosuite. Any form
|
||||
of asymmetric cryptography can be dropped in to be used instead of the default.
|
||||
Right now, there are two cryptosuites, `++crua`, which is your standard RSA,
|
||||
and `++crub`, which is elliptic curve crypto but is mostly stubbed out at the
|
||||
moment.
|
||||
|
||||
####`++as:acru`, asymmetric operations
|
||||
|
||||
```
|
||||
++ as ^? :: asym ops
|
||||
|% ++ seal |=([a=pass b=@ c=@] _@) :: encrypt to a
|
||||
++ sign |=([a=@ b=@] _@) :: certify as us
|
||||
++ sure |=([a=@ b=@] *(unit ,@)) :: authenticate from us
|
||||
++ tear |= [a=pass b=@] :: accept from a
|
||||
*(unit ,[p=@ q=@]) ::
|
||||
-- ::
|
||||
```
|
||||
|
||||
This is the core that defines the standard asymmetric cryptography
|
||||
operations.
|
||||
|
||||
`++seal:as:acru` allows us to send a message encrypted with someone's public
|
||||
key so that only they may read it. If Alice seals a message with Bob's public
|
||||
key, then she can be sure that Bob is the only one who can read it. This is
|
||||
associated with the `++skin` `%full`.
|
||||
|
||||
`++sign:as:acru` allows us to sign a message with our private key so that
|
||||
others can verify that we sent the message. If Alice signs a message with her
|
||||
private key, then Bob can verify with her public key that it was indeed Alice
|
||||
who sent it. This is associated with the `++skin` `%open`.
|
||||
|
||||
`++sure:as:acru` is the dual to `++sign:as:acru`. It allows us to verify that
|
||||
a message we have received is indeed from the claimed sender. If Alice sends a
|
||||
message with her private key, then Bob can use this arm to verify that it was
|
||||
indeed Alice who sent it. This is associated with the `++skin` `%open`.
|
||||
|
||||
`++tear:as:acru` is the dual to `++seal:as:acru`. It allows us to read a
|
||||
message that we can be sure is only read by us. If Alice seals a message with
|
||||
Bob's public key, then Bob can use this arm to read it. This is associated
|
||||
with the `++skin` `%full`.
|
||||
|
||||
####`++de:acru`, `++dy:acru`, and `++en:acru`, symmetric encryption/decryption
|
||||
|
||||
```
|
||||
++ de |+([a=@ b=@] *(unit ,@)) :: symmetric de, soft
|
||||
++ dy |+([a=@ b=@] _@) :: symmetric de, hard
|
||||
++ en |+([a=@ b=@] _@) :: symmetric en
|
||||
```
|
||||
|
||||
Symmetric encryption is associated with the `++skin` `%fast`.
|
||||
|
||||
`++de:acru` decrypts a message with a symmetric key, returning `~` on failure
|
||||
and `[~ u=data]` on success.
|
||||
|
||||
`++dy:acru` decrypts a message with a symmetric key, crashing on failure. This
|
||||
should almost always be defined as, and should always be semantically
|
||||
equivalent to, `(need (de a b))`.
|
||||
|
||||
`++en:acru` encrypts a message with a symmetric key.
|
||||
|
||||
####`++ex:acru`, exporting data
|
||||
|
||||
```
|
||||
++ ex ^? :: export
|
||||
|% ++ fig _@uvH :: fingerprint
|
||||
++ pac _@uvG :: default passcode
|
||||
++ pub *pass :: public key
|
||||
++ sec *ring :: private key
|
||||
--
|
||||
```
|
||||
|
||||
`++fig:ex:acru` is our fingerprint, usually a hash of our public key. This is
|
||||
used, for example, in `++zeno`, where every carrier owner's fingerprint is
|
||||
stored so that we can ensure that carriers are indeed owned by their owners
|
||||
|
||||
`++pac:ex:acru` is our default passcode, which is unused at present.
|
||||
|
||||
`++pub:ex:acru` is the `++pass` form of our public key.
|
||||
|
||||
`++sec:ex:acru` is the `++ring` form of our private key.
|
||||
|
||||
####`++nu:acru`, reconstructors
|
||||
|
||||
```
|
||||
++ nu ^? :: reconstructors
|
||||
|% ++ pit |=([a=@ b=@] ^?(..nu)) :: from [width seed]
|
||||
++ nol |=(a=@ ^?(..nu)) :: from naked ring
|
||||
++ com |=(a=@ ^?(..nu)) :: from naked pass
|
||||
--
|
||||
```
|
||||
|
||||
These arms allow us to reconstruct a `++acru` from basic data.
|
||||
|
||||
`++pit:nu:acru` constructs a `++acru` from the width of our intended key and
|
||||
seed entropy. This is usually used in the initial construction of the
|
||||
`++acru`.
|
||||
|
||||
`++nol:nu:acru` constructs a `++acru` from a "naked ring", meaning a `++ring`
|
||||
without the initial byte identifying the type of crypto. There is often a
|
||||
helper arm that that wraps this; see `++weur` for `++crua` and `++wear` for
|
||||
`++crub`.
|
||||
|
||||
`++com:nu:acru` constructs a `++acru` from a "naked pass", meaning a `++ring`
|
||||
without the initial byte identifying the type of crypto. There is often a
|
||||
helper arm that that wraps this; see `++haul` for `++crua` and `++hail` for
|
||||
`++crub`.
|
||||
|
||||
###`++will`, certificate
|
||||
|
||||
```
|
||||
++ will (list deed) :: certificate
|
||||
```
|
||||
|
||||
This is a list of deeds associated with the current ship. There should be
|
||||
an item in this list for every ship from this point up in the hierarchy times
|
||||
the number of lives that each ship has had. For example, ~hoclur-bicrel may
|
||||
have a will with three items: one for itself, one for ~tasruc (who issued
|
||||
~hoclur-bicrel's deed) and one for ~tug (who issued ~tasruc's deed).
|
||||
|
||||
###`++deed`, identity
|
||||
|
||||
```
|
||||
++ deed ,[p=@ q=step r=?] :: sig, stage, fake?
|
||||
```
|
||||
|
||||
`p` is the signature of a particular deed, which is a signed copy of `q`.
|
||||
|
||||
`q` is the stage in the identity.
|
||||
|
||||
`r` is true if we're working on a fake network, where we don't check that the
|
||||
carrier fingerprints are correct. This allows us to create fake networks for
|
||||
development without interfering with the real network.
|
||||
|
||||
###`++step`, identity stage
|
||||
|
||||
```
|
||||
++ step ,[p=bray q=gens r=pass] :: identity stage
|
||||
```
|
||||
|
||||
This is a single stage in our identity. Thus, this is specific to a single
|
||||
life in a single ship. Everything in here may change between lives.
|
||||
|
||||
`p`
|
||||
|
||||
`q`
|
||||
|
||||
`r` is the public key for this stage in the identity.
|
||||
|
||||
###`++bray`
|
||||
|
||||
```
|
||||
++ bray ,[p=life q=(unit life) r=ship s=@da] :: our parent us now
|
||||
```
|
||||
|
||||
XXX
|
||||
|
||||
###`++gens`, general identity
|
||||
|
||||
```
|
||||
++ gens ,[p=lang q=gcos] :: general identity
|
||||
```
|
||||
|
||||
`p` is the IETF language code for the preferred language of this identity.
|
||||
This is unused at the moment, but in the future text should be localized based
|
||||
on this.
|
||||
|
||||
`q` is the description of the ship.
|
||||
|
||||
###`++gcos`, identity description
|
||||
|
||||
```
|
||||
++ gcos :: id description
|
||||
$% [%czar ~] :: 8-bit ship
|
||||
[%duke p=what] :: 32-bit ship
|
||||
[%earl p=@t] :: 64-bit ship
|
||||
[%king p=@t] :: 16-bit ship
|
||||
[%pawn p=(unit ,@t)] :: 128-bit ship
|
||||
== ::
|
||||
```
|
||||
|
||||
This is the description of the identity of a ship. Most types of identity have
|
||||
a `@t` field, which is their human-readable name. The identity of a `%duke` is
|
||||
more involved.
|
||||
|
||||
A `%czar`, a carrier, is a ship with an 8-bit address. Thus, there are only
|
||||
256 carriers. These are at the top of the namespace hierarchy, and the
|
||||
fingerprint of each carrier is stored in `++zeno`. These are the "senators" of
|
||||
Urbit.
|
||||
|
||||
A `%king`, a cruiser, is a ship with a 16-bit address. Thus, there are 65,536
|
||||
cruisers. Each carrier may issue 256 cruisers. These are the infrastructure
|
||||
of Urbit.
|
||||
|
||||
A `%duke`, a destroyer, is a ship with a 32-bit address. Thus, there are
|
||||
4,294,967,296 destroyers. Each cruiser may issue 65,536 cruisers. These are
|
||||
the individuals of Urbit.
|
||||
|
||||
A `%earl`, a yacht, is a ship with a 64-bit address. Thus, there are
|
||||
18,446,744,073,709,551,616 yachts. Each destroyer may issue 4,294,967,296
|
||||
yachts. These are the devices of Urbit.
|
||||
|
||||
A `%pawn`, a submarine, is a ship with a 128-bit address. Thus, there are a
|
||||
lot of submarines. The chance of random name collision is negligible, so
|
||||
submarines are not issued by any ship. They must simply assert their presence,
|
||||
and they are all considered children of ~zod. This is the underworld of Urbit,
|
||||
where anonymity reigns supreme.
|
||||
|
||||
###`++what`, logical destroyer identity
|
||||
|
||||
```
|
||||
++ what :: logical identity
|
||||
$% [%anon ~] :: anonymous
|
||||
[%lady p=whom] :: female person ()
|
||||
[%lord p=whom] :: male person []
|
||||
[%punk p=sect q=@t] :: opaque handle ""
|
||||
== ::
|
||||
```
|
||||
|
||||
This is the logical identity of a destroyer.
|
||||
|
||||
A `%anon` is a completely anonymous destroyer. The difference between this and
|
||||
a submarine is that a submarine is ephemeral while a `%anon` destroyer is not.
|
||||
Thus, we may not know who ~hoclur-bicrel is, but we do know that it's always
|
||||
the same person.
|
||||
|
||||
A `%lady` is a female person. The name used here should be a real name.
|
||||
|
||||
A `%lord` is a male person. The name used here should be a real name.
|
||||
|
||||
A `%punk` is a person who is identified only by a handle.
|
||||
|
||||
###`++whom`, real person
|
||||
|
||||
```
|
||||
++ whom ,[p=@ud q=govt r=sect s=name] :: year/govt/id
|
||||
```
|
||||
|
||||
Ths is the information associated with a real person. It is mostly information
|
||||
that could be observed with the briefest of interactions.
|
||||
|
||||
`p` is the birth year.
|
||||
|
||||
`q` is the location of a user, usually of the form "country/zip".
|
||||
|
||||
`r` is the sect of the user.
|
||||
|
||||
`s` is the real name of the person.
|
||||
|
||||
|
||||
###`++govt`
|
||||
|
||||
```
|
||||
++ govt path :: country/postcode
|
||||
```
|
||||
|
||||
This is the location of the user, usually of the form "country/zip".
|
||||
|
||||
###`++sect`
|
||||
|
||||
```
|
||||
++ sect ?(%black %blue %red %orange %white) :: banner
|
||||
```
|
||||
|
||||
XXX
|
||||
|
||||
###`++name`
|
||||
|
||||
```
|
||||
++ name ,[p=@t q=(unit ,@t) r=(unit ,@t) s=@t] :: first mid/nick last
|
||||
```
|
||||
|
||||
This is the given name, possible middle name/initial, possible nickname, and
|
||||
surname of a user.
|
||||
|
||||
packet format
|
||||
-------------
|
||||
|
||||
`++go`, PKI engine
|
||||
------------------
|
||||
|
||||
###`++as`, per server
|
||||
|
||||
####`++born`, register user
|
||||
|
||||
#####`++lax`, per client
|
||||
|
||||
`++pu`, packet pump
|
||||
-------------------
|
||||
|
||||
`++am`, protocol engine
|
||||
-----------------------
|
||||
|
||||
###`++um`, per server
|
||||
|
||||
####`++ho`, per friend
|
||||
|
||||
#####`++la`, per packet
|
||||
|
||||
protocol vane
|
||||
-------------
|
||||
|
||||
#Batz
|
||||
|
||||
Coming soon
|
||||
|
||||
#Dill
|
||||
|
||||
Coming soon
|
||||
|
||||
#Clay
|
||||
|
||||
Clay
|
||||
====
|
||||
|
||||
Clay is our filesystem.
|
||||
|
||||
data models
|
||||
-----------
|
||||
|
||||
###`++raft`, formal state
|
||||
|
||||
```
|
||||
++ raft :: filesystem
|
||||
$: fat=(map ship room) :: domestic
|
||||
hoy=(map ship rung) :: foreign
|
||||
ran=rang :: hashes
|
||||
== ::
|
||||
```
|
||||
|
||||
This is the state of our vane. Anything that must be remembered between calls
|
||||
to clay must be stored in this state.
|
||||
|
||||
`fat` is the set of domestic servers. This stores all the information that is
|
||||
specfic to a particular ship on this pier. The keys to this map are the ships
|
||||
on the current pier.
|
||||
|
||||
`hoy` is the set of foreign servers that we know anything about. This stores
|
||||
all the information that is specific to a particular foreign ship. The keys to
|
||||
this map are all the ships whose filesystems we have attempted to access
|
||||
through clay.
|
||||
|
||||
`ran` is the store of all commits and deltas, keyed by hash. The is where all
|
||||
the "real" data we know is stored; the rest is "just bookkeeping".
|
||||
|
||||
###`++room`, filesystem per domestic ship
|
||||
|
||||
```
|
||||
++ room :: fs per ship
|
||||
$: hun=duct :: terminal duct
|
||||
hez=(unit duct) :: sync duch
|
||||
dos=(map desk dojo) :: native desk
|
||||
== ::
|
||||
```
|
||||
|
||||
This is the representation of the filesystem of a ship on our pier.
|
||||
|
||||
`hun` is the duct that we use to send messages to dill to display notifications
|
||||
of filesystem changes. Only `%note` gifts should be produced along this duct.
|
||||
This is set by the `%init` kiss.
|
||||
|
||||
`hez`, if present, is the duct we use to send sync messages to unix so that
|
||||
they end up in the pier unix directory. Only `%ergo` gifts should be producd
|
||||
along this duct. This is set by `%into` and `%invo` gifts.
|
||||
|
||||
`dos` is a well-known operating system released in 1981. It is also the set of
|
||||
desks on this ship, mapped to their data.
|
||||
|
||||
###`++desk`, filesystem branch
|
||||
|
||||
```
|
||||
++ desk ,@tas :: ship desk case spur
|
||||
```
|
||||
|
||||
This is the name of a branch of the filesystem. The default desks are "arvo",
|
||||
"main", and "try". More may be created by simply referencing them. Desks have
|
||||
independent histories and states, and they may be merged into each other.
|
||||
|
||||
###`++dojo`, domestic desk state
|
||||
|
||||
```
|
||||
++ dojo ,[p=cult q=dome] :: domestic desk state
|
||||
```
|
||||
|
||||
This is the all the data that is specific to a particular desk on a domestic
|
||||
ship. `p` is the set of subscribers to this desk and `q` is the data in the
|
||||
desk.
|
||||
|
||||
###`++cult`, subscriptions
|
||||
|
||||
```
|
||||
++ cult (map duct rave) :: subscriptions
|
||||
```
|
||||
|
||||
This is the set of subscriptions to a particular desk. The keys are the ducts
|
||||
from where the subscriptions requests came. The results will be produced along
|
||||
these ducts. The values are a description of the requested information.
|
||||
|
||||
###`++rave`, general subscription request
|
||||
|
||||
```
|
||||
++ rave :: general request
|
||||
$% [& p=mood] :: single request
|
||||
[| p=moat] :: change range
|
||||
== ::
|
||||
```
|
||||
|
||||
This represents a subscription request for a desk. The request can be for
|
||||
either a single item in the desk or else for a range of changes on the desk.
|
||||
|
||||
###`++mood`, single subscription request
|
||||
|
||||
```
|
||||
++ mood ,[p=care q=case r=path] :: request in desk
|
||||
```
|
||||
|
||||
This represents a request for the state of the desk at a particular commit,
|
||||
specfied by `q`. `p` specifies what kind of information is desired, and `r`
|
||||
specifies the path we are requesting.
|
||||
|
||||
###`++moat`, range subscription request
|
||||
|
||||
```
|
||||
++ moat ,[p=case q=case] :: change range
|
||||
```
|
||||
|
||||
This represents a request for all changes between `p` and `q`. Note that there
|
||||
is currently no way to request to be notified only on changes to particular
|
||||
paths in the filesystem. You must subscribe to the entire desk.
|
||||
|
||||
###`++care`, clay submode
|
||||
|
||||
```
|
||||
++ care ?(%u %v %w %x %y %z) :: clay submode
|
||||
```
|
||||
|
||||
This specifies what type of information is requested in a subscription or a
|
||||
scry.
|
||||
|
||||
`%u` requests the `++rang` at the current moment. Because this information is
|
||||
not stored for any moment other than the present, we crash if the `++case` is
|
||||
not a `%da` for now.
|
||||
|
||||
`%v` requests the `++dome` at the specified commit.
|
||||
|
||||
`%w` requests the current revsion number of the desk.
|
||||
|
||||
`%x` requests the file at a specified path at the specified commit. If there
|
||||
is no node at that path or if the node has no contents (that is, if `q:ankh` is
|
||||
null), then this produces null.
|
||||
|
||||
`%y` requests a `++arch` of the specfied commit at the specified path.
|
||||
|
||||
`%z` requests the `++ankh` of the specified commit at the specfied path.
|
||||
|
||||
###`++arch`, shallow filesystem node
|
||||
|
||||
```
|
||||
++ arch ,[p=@uvI q=(unit ,@uvI) r=(map ,@ta ,~)] :: fundamental node
|
||||
```
|
||||
|
||||
This is analogous to `++ankh` except that the we have neither our contents nor
|
||||
the ankhs of our children. The other fields are exactly the same, so `p` is a
|
||||
hash of the associated ankh, `u.q`, if it exists, is a hash of the contents of
|
||||
this node, and the keys of `r` are the names of our children. `r` is a map to
|
||||
null rather than a set so that the ordering of the map will be equivalent to
|
||||
that of `r:ankh`, allowing efficient conversion.
|
||||
|
||||
###`++case`, specifying a commit
|
||||
|
||||
```
|
||||
++ case :: ship desk case spur
|
||||
$% [%da p=@da] :: date
|
||||
[%tas p=@tas] :: label
|
||||
[%ud p=@ud] :: number
|
||||
== ::
|
||||
```
|
||||
|
||||
A commit can be referred to in three ways: `%da` refers to the commit that was
|
||||
at the head on date `p`, `%tas` refers to the commit labeled `p`, and `%ud`
|
||||
refers to the commit numbered `p`. Note that since these all can be reduced
|
||||
down to a `%ud`, only numbered commits may be referenced with a `++case`.
|
||||
|
||||
###`++dome`, desk data
|
||||
|
||||
```
|
||||
++ dome :: project state
|
||||
$: ang=agon :: pedigree
|
||||
ank=ankh :: state
|
||||
let=@ud :: top id
|
||||
hit=(map ,@ud tako) :: changes by id
|
||||
lab=(map ,@tas ,@ud) :: labels
|
||||
== ::
|
||||
```
|
||||
|
||||
This is the data that is actually stored in a desk.
|
||||
|
||||
`ang` is unused and should be removed.
|
||||
|
||||
`ank` is the current state of the desk. Thus, it is the state of the
|
||||
filesystem at revison `let`. The head of a desk is always a numbered commit.
|
||||
|
||||
`let` is the number of the most recently numbered commit. This is also the
|
||||
total number of numbered commits.
|
||||
|
||||
`hit` is a map of numerical ids to hashes of commits. These hashes are mapped
|
||||
into their associated commits in `hut:rang`. In general, the keys of this map
|
||||
are exactly the numbers from 1 to `let`, with no gaps. Of course, when there
|
||||
are no numbered commits, `let` is 0, so `hit` is null. Additionally, each of
|
||||
the commits is an ancestor of every commit numbered greater than this one.
|
||||
Thus, each is a descendant of every commit numbered less than this one. Since
|
||||
it is true that the date in each commit (`t:yaki`) is no earlier than that of
|
||||
each of its parents, the numbered commits are totally ordered in the same way
|
||||
by both pedigree and date. Of course, not every commit is numbered. If that
|
||||
sounds too complicated to you, don't worry about it. It basically behaves
|
||||
exactly as you would expect.
|
||||
|
||||
`lab` is a map of textual labels to numbered commits. Note that labels can
|
||||
only be applied to numbered commits. Labels must be unique across a desk.
|
||||
|
||||
###`++ankh`, filesystem node
|
||||
|
||||
```
|
||||
++ ankh :: fs node (new)
|
||||
$: p=cash :: recursive hash
|
||||
q=(unit ,[p=cash q=*]) :: file
|
||||
r=(map ,@ta ankh) :: folders
|
||||
== ::
|
||||
```
|
||||
|
||||
This is a single node in the filesystem. This may be file or a directory or
|
||||
both. In earth filesystems, a node is a file xor a directory. On mars, we're
|
||||
inclusive, so a node is a file ior a directory.
|
||||
|
||||
`p` is a recursive hash that depends on the contents of the this file or
|
||||
directory and on any children.
|
||||
|
||||
`q` is the contents of this file, if any. `p.q` is a hash of the contents
|
||||
while `q.q` is the data itself.
|
||||
|
||||
`r` is the set of children of this node. In the case of a pure file, this is
|
||||
empty. The keys are the names of the children and the values are, recursively,
|
||||
the nodes themselves.
|
||||
|
||||
###`++cash`, ankh hash
|
||||
|
||||
```
|
||||
++ cash ,@uvH :: ankh hash
|
||||
```
|
||||
|
||||
This is a 128-bit hash of an ankh. These are mostly stored within ankhs
|
||||
themselves, and they are used to check for changes in possibly-deep
|
||||
hierarchies.
|
||||
|
||||
###`++rung`, filesystem per neighbor ship
|
||||
|
||||
```
|
||||
++ rung $: rus=(map desk rede) :: neighbor desks
|
||||
== ::
|
||||
```
|
||||
|
||||
This is the filesystem of a neighbor ship. The keys to this map are all the
|
||||
desks we know about on their ship.
|
||||
|
||||
###`++rede`, desk state
|
||||
|
||||
```
|
||||
++ rede :: universal project
|
||||
$: lim=@da :: complete to
|
||||
qyx=cult :: subscribers
|
||||
ref=(unit rind) :: outgoing requests
|
||||
dom=dome :: revision state
|
||||
== ::
|
||||
```
|
||||
|
||||
This is our knowledge of the state of a desk, either foreign or domestic.
|
||||
|
||||
`lim` is the date of the last full update. We only respond to requests for
|
||||
stuff before this time.
|
||||
|
||||
`qyx` is the list of subscribers to this desk. For domestic desks, this is
|
||||
simply `p:dojo`, all subscribers to the desk, while in foreign desks this is
|
||||
all the subscribers from our ship to the foreign desk.
|
||||
|
||||
`ref` is the request manager for the desk.
|
||||
|
||||
`dom` is the actual data in the desk.
|
||||
|
||||
###`++rind`, request manager
|
||||
|
||||
```
|
||||
++ rind :: request manager
|
||||
$: nix=@ud :: request index
|
||||
bom=(map ,@ud ,[p=duct q=rave]) :: outstanding
|
||||
fod=(map duct ,@ud) :: current requests
|
||||
haw=(map mood (unit)) :: simple cache
|
||||
== ::
|
||||
```
|
||||
|
||||
This is the request manager for a desk.
|
||||
|
||||
`nix` is one more than the index of the most recent request. Thus, it is the
|
||||
next available request number.
|
||||
|
||||
`bom` is the set of outstanding requests. The keys of this map are some subset
|
||||
of the numbers between 0 and one less than `nix`. The members of the map are
|
||||
exactly those requests that have not yet been fully satisfied.
|
||||
|
||||
`fod` is the same set as `bom`, but from a different perspective. In
|
||||
particular, the values of `fod` are the same as the values of `bom`, and the
|
||||
`p` out of the values of `bom` are the same as the keys of `fod`. Thus, we can
|
||||
map ducts to their associated request number and `++rave`, and we can map
|
||||
numbers to their associated duct and `++rave`.
|
||||
|
||||
`haw` is a map from simple requests to their values. This acts as a cache for
|
||||
requests that have already been made. Thus, the second request for a
|
||||
particular `++mood` is nearly instantaneous.
|
||||
|
||||
###`++rang`, data store
|
||||
|
||||
```
|
||||
++ rang $: hut=(map tako yaki) ::
|
||||
lat=(map lobe blob) ::
|
||||
== ::
|
||||
```
|
||||
|
||||
This is a set of data keyed by hash. Thus, this is where the "real" data is
|
||||
stored, but it is only meaningful if we know the hash of what we're looking
|
||||
for.
|
||||
|
||||
`hut` is a map from hashes to commits. We often get the hashes from
|
||||
`hit:dome`, which keys them by logical id. Not every commit has an id.
|
||||
|
||||
`lat` is a map from hashes to the actual data. We often get the hashes from a
|
||||
`++yaki`, a commit, which references this map to get the data. There is no
|
||||
`++blob` in any `++yaki`. They are only accessible through this map.
|
||||
|
||||
###`++tako`, commit reference
|
||||
|
||||
```
|
||||
++ tako ,@ :: yaki ref
|
||||
```
|
||||
|
||||
This is a hash of a `++yaki`, a commit. These are most notably used as the
|
||||
keys in `hut:rang`, where they are associated with the actual `++yaki`, and as
|
||||
the values in `hit:dome`, where sequential ids are associated with these.
|
||||
|
||||
###`++yaki`, commit
|
||||
|
||||
```
|
||||
++ yaki ,[p=(list tako) q=(map path lobe) r=tako t=@da] :: commit
|
||||
```
|
||||
|
||||
This is a single commit.
|
||||
|
||||
`p` is a list of the hashes of the parents of this commit. In most cases, this
|
||||
will be a single commit, but in a merge there may be more parents. In theory,
|
||||
there may be an arbitrary number of parents, but in practice merges have
|
||||
exactly two parents. This may change in the future. For commit 1, there is no
|
||||
parent.
|
||||
|
||||
`q` is a map of the paths on a desk to the data at that location. If you
|
||||
understand what a `++lobe` and a `++blob` is, then the type signature here
|
||||
tells the whole story.
|
||||
|
||||
`r` is the hash associated with this commit.
|
||||
|
||||
`t` is the date at which this commit was made.
|
||||
|
||||
###`++lobe`, data reference
|
||||
|
||||
```
|
||||
++ lobe ,@ :: blob ref
|
||||
```
|
||||
|
||||
This is a hash of a `++blob`. These are most notably used in `lat:rang`, where
|
||||
they are associated with the actual `++blob`, and as the values in `q:yaki`,
|
||||
where paths are associated with their data in a commit.
|
||||
|
||||
###`++blob`, data
|
||||
|
||||
```
|
||||
++ blob $% [%delta p=lobe q=lobe r=udon] :: delta on q
|
||||
[%direct p=lobe q=* r=umph] ::
|
||||
[%indirect p=lobe q=* r=udon s=lobe] ::
|
||||
== ::
|
||||
```
|
||||
|
||||
This is a node of data. In every case, `p` is the hash of the blob.
|
||||
|
||||
`%delta` is the case where we define the data by a delta on other data. In
|
||||
practice, the other data is always the previous commit, but nothing depends on
|
||||
this. `q` is the hash of the parent blob, and `r` is the delta.
|
||||
|
||||
`%direct` is the case where we simply have the data directly. `q` is the data
|
||||
itself, and `r` is any preprocessing instructions. These almost always come
|
||||
from the creation of a file.
|
||||
|
||||
`%indirect` is both of the preceding cases at once. `q` is the direct data,
|
||||
`r` is the delta, and `s` is the parent blob. It should always be the case
|
||||
that applying `r` to `s` gives the same data as `q` directly (with the
|
||||
prepreprocessor instructions in `p.r`). This exists purely for performance
|
||||
reasons. This is unused, at the moment, but in general these should be created
|
||||
when there are a long line of changes so that we do not have to traverse the
|
||||
delta chain back to the creation of the file.
|
||||
|
||||
###`++udon`, abstract delta
|
||||
|
||||
```
|
||||
++ udon :: abstract delta
|
||||
$: p=umph :: preprocessor
|
||||
$= q :: patch
|
||||
$% [%a p=* q=*] :: trivial replace
|
||||
[%b p=udal] :: atomic indel
|
||||
[%c p=(urge)] :: list indel
|
||||
[%d p=upas q=upas] :: tree edit
|
||||
== ::
|
||||
== ::
|
||||
```
|
||||
|
||||
This is an abstract change to a file. This is a superset of what would
|
||||
normally be called diffs. Diffs usually refer to changes in lines of text
|
||||
while we have the ability to do more interesting deltas on arbitrary data
|
||||
structures.
|
||||
|
||||
`p` is any preprocessor instructions.
|
||||
|
||||
`%a` refers to the trival delta of a complete replace of old data with new
|
||||
data.
|
||||
|
||||
`%b` refers to changes in an opaque atom on the block level. This has very
|
||||
limited usefulness, and is not used at the moment.
|
||||
|
||||
`%c` refers to changes in a list of data. This is often lines of text, which
|
||||
is your classic diff. We, however, will work on any list of data.
|
||||
|
||||
`%d` refers to changes in a tree of data. This is general enough to describe
|
||||
changes to any hoon noun, but often more special-purpose delta should be
|
||||
created for different content types. This is not used at the moment, and may
|
||||
in fact be unimplemented.
|
||||
|
||||
###`++urge`, list change
|
||||
|
||||
```
|
||||
++ urge |*(a=_,* (list (unce a))) :: list change
|
||||
```
|
||||
|
||||
This is a parametrized type for list changes. For example, `(urge ,@t)` is a
|
||||
list change for lines of text.
|
||||
|
||||
###`++unce`, change part of a list.
|
||||
|
||||
```
|
||||
++ unce |* a=_,* :: change part
|
||||
$% [%& p=@ud] :: skip[copy]
|
||||
[%| p=(list a) q=(list a)] :: p -> q[chunk]
|
||||
== ::
|
||||
```
|
||||
|
||||
This is a single change in a list of elements of type `a`. For example, `(unce ,@t)` is
|
||||
a single change in a lines of text.
|
||||
|
||||
`%&` means the next `p` lines are unchanged.
|
||||
|
||||
`%|` means the lines `p` have changed to `q`.
|
||||
|
||||
###`++umph`, preprocessing information
|
||||
|
||||
```
|
||||
++ umph :: change filter
|
||||
$| $? %a :: no filter
|
||||
%b :: jamfile
|
||||
%c :: LF text
|
||||
== ::
|
||||
$% [%d p=@ud] :: blocklist
|
||||
== ::
|
||||
```
|
||||
|
||||
This space intentionally left undocumented. This stuff will change once we get
|
||||
a well-typed clay.
|
||||
|
||||
|
||||
###`++upas`, tree change
|
||||
|
||||
```
|
||||
++ upas :: tree change (%d)
|
||||
$& [p=upas q=upas] :: cell
|
||||
$% [%0 p=axis] :: copy old
|
||||
[%1 p=*] :: insert new
|
||||
[%2 p=axis q=udon] :: mutate!
|
||||
== ::
|
||||
```
|
||||
|
||||
This space intentionally left undocumented. This stuff is not known to work,
|
||||
and will likely change when we get a well-typed clay. Also, this is not a
|
||||
complicated type; it is not difficult to work out the meaning.
|
||||
|
||||
#Eyre
|
||||
|
||||
Coming soon
|
||||
|
||||
#Ford
|
||||
|
||||
Coming soon
|
||||
|
||||
#Gall
|
||||
|
||||
Coming soon
|
1379
main/pub/src/doc/say/arvo/app.md
Normal file
1379
main/pub/src/doc/say/arvo/app.md
Normal file
File diff suppressed because it is too large
Load Diff
522
main/pub/src/doc/say/arvo/pub2.md
Normal file
522
main/pub/src/doc/say/arvo/pub2.md
Normal file
@ -0,0 +1,522 @@
|
||||
# Urbit is a global functional filesystem.
|
||||
|
||||
The whole idea of a filesystem is that a file is a function of a
|
||||
path. If it's an *immutable* filesystem like Urbit, the function
|
||||
(though partial), once bound never changes.
|
||||
|
||||
This is kind of neat. What'd be really neat, though, is if the
|
||||
namespace operator, rather than just returning one static file,
|
||||
produced an immutable function on your whole filesystem. Or
|
||||
better yet, on *all visible data in the world*.
|
||||
|
||||
It would also be ideal if the product of this function was not
|
||||
just a binary blob, but a *typed data structure*. Even better -
|
||||
the product might not be data, but code, or a mix of data and
|
||||
code - a function, an object, a library, even an application.
|
||||
|
||||
What's basically irritating about the Web is that it's almost
|
||||
this, but it's not this. The Web is almost a web-of-data, but
|
||||
it's not a web-of-data. So it does not scale as a substrate for
|
||||
complex higher-level communication patterns, and even for simple
|
||||
use cases breaks frequently in production.
|
||||
|
||||
The road to a real *global functional filesystem* is perfectly
|
||||
straightforward in principle. If a function of one path is
|
||||
immutable, so is a function of two, or n. More generally, any
|
||||
immutable function of a filename is a referentially transparent
|
||||
name - a concept familiar in many functional languages.
|
||||
|
||||
We already put source files in immutable filesystems, which we
|
||||
call "revision-control systems." To convert this design into a
|
||||
functional filesystem, all we have to do is define a precise
|
||||
functional semantics for these source files, including in that
|
||||
semantics the power to read back into the namespace.
|
||||
|
||||
In principle this is quite straightforward. We need exactly two
|
||||
components for our functional filesystem: an immutable filesystem
|
||||
and a functional language. Neither are particularly exotic bits
|
||||
of system software kit.
|
||||
|
||||
But they should be made to fit each other. Could you strap
|
||||
github to Haskell and make a global functional filesystem? Well,
|
||||
sort of. Certainly in principle. You could also strap a car
|
||||
engine to a mountain bike, and make a motorcycle. It wouldn't be
|
||||
a very good motorcycle.
|
||||
|
||||
Urbit is not actually a very good motorcycle. Not yet, anyway.
|
||||
It is a motorcycle, though. Let's take it for a spin.
|
||||
|
||||
|
||||
# Get your urbit.
|
||||
|
||||
First, grab yourself a "destroyer" at urbit.org. You'll come
|
||||
away with an phonemic urbit name, or "ship" - mine is
|
||||
`~tasfyn-partyv`. Let's say `~lacser-dovlet` is yours.
|
||||
|
||||
As the length of the string suggests, here are only 2^32
|
||||
destroyers, so we have to ration them. You can skip this step,
|
||||
but only at the expense of urbiting from a mere "submarine," a
|
||||
self-generated 128-bit ship - like:
|
||||
|
||||
~bacbyl-dabdyr-marfel-palnep--tompex-nibnym-sidpet-ticfus
|
||||
|
||||
In theory a submarine can do anything a destroyer can. In
|
||||
practice, if you have any interest in networking with other
|
||||
ships - don't be surprised if they assume you're a bot.
|
||||
|
||||
Obviously, your ship is the root of your own personal namespace.
|
||||
`~lacser-dovlet` is your "me function." Urbit, as a global
|
||||
functional filesystem, is the union of everyone's me function.
|
||||
|
||||
(Urbit should not be confused with distributed storage systems,
|
||||
like Freenet or IPFS. In Urbit all your data is on your own
|
||||
computer, though it may be a virtual computer in the cloud.)
|
||||
|
||||
|
||||
# The shape of urbitspace.
|
||||
|
||||
But wait. How, exactly, are these shorter names allocated?
|
||||
|
||||
As Samuel L. Jackson once put it: for money, bitch! But you're
|
||||
an early adopter and we love you. So free ships for you, your
|
||||
friends, and everyone.
|
||||
|
||||
Seriously - it will be a *long* time, if ever, before you have
|
||||
any trouble getting a free Urbit destroyer, from us or anyone.
|
||||
The main thing we want to make sure is that we're giving them
|
||||
out mostly to unique humans. In the long run, this is quite hard
|
||||
on the Internets. We're still small so no abuser gives a hoot.
|
||||
|
||||
And we (the evil Tlon Corporation, to be exact) are *not* the
|
||||
only people who *can* give away destroyers, just the only people
|
||||
doing it right now. Actually, some of the people who hold this
|
||||
right are kind of our enemies - or at least, disgruntled former
|
||||
employees (of the evil Tlon Corporation). They obviously can't
|
||||
be trusted to collude in our evil schemes! So if you fear us,
|
||||
you're probably safe with them.
|
||||
|
||||
Moreover, more importantly: once you own a destroyer (or more
|
||||
realistically, once you sail it away from our harbor), it's
|
||||
cryptographically yours and we (the evil Tlon Corporation) have
|
||||
no practical way to steal it back, spy on you, etc. No, Urbit
|
||||
isn't just another way of selling yourself into digital serfdom.
|
||||
|
||||
It's also important not to confuse Urbit with a cryptocurrency.
|
||||
Urbitspace is digital land, not digital money. It is more like
|
||||
the DNS than like Bitcoin. As in real estate, Urbit transactions
|
||||
are unusual and have high frictional cost.
|
||||
|
||||
Making Urbit a bad currency is a feature, not a bug. Urbit is
|
||||
designed to be neither particularly easy for governments to
|
||||
control, nor particularly useful for criminals to abuse. Both
|
||||
are difficult goals to hit, but they go well together. Any
|
||||
system that attracts criminals will inherently also attract their
|
||||
natural predator.
|
||||
|
||||
Also, as with land, the price of urbitspace is driven by direct
|
||||
utility, not just collective speculation. And the only way land
|
||||
is ever given away for free is as a homestead. Your free urbit
|
||||
is for use, not for investment. Urbitspace will be worth
|
||||
something if and only if it's useful to someone.
|
||||
|
||||
|
||||
# More namespace design digressions.
|
||||
|
||||
Feel free to skip these if you really couldn't care less why.
|
||||
|
||||
Why are ship names synthetic strings, not user-chosen strings?
|
||||
The simplest way to assign handles is with a central authority,
|
||||
like Facebook. If you like being a digital serf, please, you
|
||||
already know where to go. But even if you issue handles with a
|
||||
proof-of-work scheme (like Namecoin), you still have a name rush.
|
||||
And a decentralized system can have no way of negotiating with
|
||||
real-world name owners whose existing claims conflict.
|
||||
|
||||
Also, there are concrete social benefits to a completely
|
||||
impersonal address. An Urbit name is like a street name. It
|
||||
doesn't mean anything and is not intended to. Even if it
|
||||
accidentally means something, everyone ignores the accident -
|
||||
when we get directions to a Bush Street or a Clinton Road, we
|
||||
rarely even think of thinking of the politicians. Worse, handles
|
||||
in real life tend to be just lame.
|
||||
|
||||
Why use short names at all, not just hashes? Why destroyers?
|
||||
Why doesn't everyone just sail the Urbit seas in a submarine?
|
||||
|
||||
Two very important reasons. One, with only 2^32 names, an Urbit
|
||||
name can be as memorable as a natural human name, or at least a
|
||||
name in a foreign language. Two, when we have a limited supply
|
||||
of names, network abusers don't find it so easy to disappear and
|
||||
reappear under a new name.
|
||||
|
||||
(In fact we're very confident that it will be possible to keep
|
||||
Urbit spam-free in practice. This would be harder if synthetic
|
||||
short names were distributed as coins in a proof-of-work scheme.
|
||||
Since Urbit treats namespace as property from day one, with no
|
||||
"airdrop" to unproductive miners, there are no anonymous original
|
||||
owners of Urbitspace. Everyone who owns a block has an incentive
|
||||
to distributed it only to well-qualified individual users: as
|
||||
with both IP addresses and actual real estate, Urbit blocks can
|
||||
easily develop a collective bad (or good) reputation.)
|
||||
|
||||
It is of course possible to use Urbit anonymously, of course.
|
||||
But only in one way: somewhere along the decentralization chain
|
||||
from the (well-identified) developers to the user, someone has to
|
||||
transfer urbitspace and be trusted to forget the recipient. It
|
||||
is to be expected that, in order to provide this useful service,
|
||||
the intermediary will demand some trust from the upstream users -
|
||||
presumably, at least, evidence of humanity.
|
||||
|
||||
Point is: these are not the droids you're looking for. You are
|
||||
not selling yourself into digital serfdom. You are escaping from
|
||||
digital serfdom. You are not selling yourself into digital
|
||||
serfdom. You are escaping from digital serfdom. Freedom is
|
||||
slavery. Urbit is freedom. Now, please raise your right hand
|
||||
and continue with the installation process.
|
||||
|
||||
# Install the SDK.
|
||||
|
||||
(Instructions here.)
|
||||
|
||||
If you got a destroyer, this will set up a local "yacht" on your
|
||||
own computer. (Otherwise, just work out of your local
|
||||
submarine.) Your destroyer lives in the cloud, where computing
|
||||
is actually reliable. Leave it there. (You can sail it home, or
|
||||
to some other harbor, any time you like.)
|
||||
|
||||
Suppose your destroyer is `~lacser-dovlet`, and your yacht is
|
||||
`~sidpet-lacser-dovlet`. Urbit will make the directory
|
||||
`~/urb/sidpet-lacser-dovlet/`, and symlink it, for convenience,
|
||||
to `~/urb/lacser-dovlet/`.
|
||||
|
||||
This is a hotsync directory. When you edit, add or delete files
|
||||
under the *pier* directory, `~/urb/sidpet-lacser-dovlet/`, it
|
||||
syncs them automatically with your yacht, then syncs the yacht
|
||||
with `~lacser-dovlet` in the sky. Sort of like Dropbox. Except
|
||||
that (a) Dropbox is a hard disk and Urbit is a computer, and (b)
|
||||
your Urbit computer is actually, in some sense, like, yours.
|
||||
(Urbit is freedom. Repeat. Urbit is freedom.)
|
||||
|
||||
|
||||
# Post and edit a webpage.
|
||||
|
||||
Start vere:
|
||||
|
||||
$ vere ~lacser-dovlet
|
||||
|
||||
Run vim on `~/urb/lacser-dovlet/main/pub/fab/fun/one.md`, and type:
|
||||
|
||||
This is a *fun experiment* in markdown.
|
||||
|
||||
Save the file. Then point a browser at
|
||||
|
||||
https://lacser-dovlet.urbit.org/pub/fab/fun/one
|
||||
|
||||
You'll see the rendered markdown. Then, change the file to read
|
||||
|
||||
This is a *different fun experiment* in markdown.
|
||||
|
||||
Save it. Then look at your browser. Did you have to hit reload?
|
||||
*How exactly did that file get from your editor to your browser?*
|
||||
|
||||
|
||||
# Urbit filesystem basics.
|
||||
|
||||
It's difficult when looking at this system superficially to
|
||||
separate the web server from the namespace. Your namespace is
|
||||
your namespace - the web server is just a legacy translation
|
||||
layer from HTTP's rather non-functional namespace.
|
||||
|
||||
(In some senses it might make more sense to compute functionally
|
||||
from Urbit's own command line. But your browser is a much better
|
||||
renderer than anything in Urbit, and you're better at using it.)
|
||||
|
||||
We have seen two kinds of filenames go into Urbit: Unix filenames
|
||||
and URLs. Obviously, neither of these is referentially
|
||||
transparent. And neither is the same thing as an Urbit path, or
|
||||
*beam*.
|
||||
|
||||
An Urbit beam has mostly the same syntax as a Unix path. The
|
||||
beam always begins with the same three spans, `/ship/desk/case/`.
|
||||
|
||||
A ship is your computer, of course; a desk is a branch, project,
|
||||
repository, etc; a case is a version. Versions are labels,
|
||||
change numbers, or dates.
|
||||
|
||||
Besides the fact that revision control is part of the filesystem,
|
||||
the only other weird thing about Urbit is that files and
|
||||
directory are orthogonal. In Unix, you can't have one file
|
||||
`/one/two/three` and another file `/one/two/three/four`, because
|
||||
the `three` inode would have to be both a file and a directory.
|
||||
|
||||
Urbit uses different APIs for data and metadata requests, and
|
||||
directories are implicit. The directory `/one/two/three` exists if
|
||||
and only if there are files with that path as a prefix. There is
|
||||
no `mkdir`, etc.
|
||||
|
||||
Without clunky stateful directories, our normal convention is to
|
||||
use the last span of the path as the file type. So where Unix
|
||||
has `/one/two/three/four.html`, Urbit has `/one/two/three/four/html`.
|
||||
|
||||
|
||||
# Functional transformations.
|
||||
|
||||
In the test above, we started with a Unix filename, synced it
|
||||
against an Urbit beam, and served it up as a URL. Ie, we went
|
||||
from the Unix filename
|
||||
|
||||
~/urb/lacser-dovlet/main/pub/fab/fun/one.md
|
||||
|
||||
to the Urbit beam
|
||||
|
||||
/~lacser-dovlet/main/~2014.7.30/pub/fab/fun/one/md
|
||||
|
||||
to the URL
|
||||
|
||||
https://lacser-dovlet.urbit.org/pub/fab/fun/one
|
||||
|
||||
What happened internally? Without getting into too much
|
||||
detail, the clean URL above was defaulted to
|
||||
|
||||
https://lacser-dovlet.urbit.org/pub/fab/fun/one.html
|
||||
|
||||
which then made Urbit try to serve the file
|
||||
|
||||
/~lacser-dovlet/main/~2014.7.30/pub/fab/fun/one/html
|
||||
|
||||
which did not exist, but we found your markdown source
|
||||
|
||||
/~lacser-dovlet/main/~2014.7.30/pub/fab/fun/one/md
|
||||
|
||||
and parsed it into the markdown tree
|
||||
|
||||
/~lacser-dovlet/main/~2014.7.30/pub/fab/fun/one/down
|
||||
|
||||
which was converted into the HTML tree
|
||||
|
||||
/~lacser-dovlet/main/~2014.7.30/pub/fab/fun/one/hymn
|
||||
|
||||
printed out into the HTML text file
|
||||
|
||||
/~lacser-dovlet/main/~2014.7.30/pub/fab/fun/one/html
|
||||
|
||||
and finally, wrapped up as the MIME object
|
||||
|
||||
/~lacser-dovlet/main/~2014.7.30/pub/fab/fun/one/mime
|
||||
|
||||
to ship back to you with a 200. Then, when you saved `one.md`
|
||||
again, we did all this again and showed you the new document.
|
||||
(We'll again delay on how *this* happened.)
|
||||
|
||||
|
||||
# Functional construction.
|
||||
|
||||
This kind of format translation is a relatively straightforward
|
||||
use of functional FS techniques. It's definitely handy but
|
||||
doesn't really rise to the level of cool.
|
||||
|
||||
Here's something cool. Again with `vim`, edit
|
||||
|
||||
~/urb/lacser-dovlet/pub/fab/fun/two/down.hook
|
||||
|
||||
to be
|
||||
|
||||
:~ :- %par
|
||||
:~ tex/"This is a "
|
||||
emp/bent/~[tex/"fun experiment "]
|
||||
tex/"in markdown."
|
||||
== ==
|
||||
|
||||
Now, point your browser at
|
||||
|
||||
https://lacser-dovlet.urbit.org/pub/fab/fun/two
|
||||
|
||||
What did we do here? Instead of a static file containing
|
||||
markdown text, we wrote a program in Hoon that generated
|
||||
a markdown value. How did we find this? We went from
|
||||
|
||||
https://lacser-dovlet.urbit.org/pub/fab/fun/two.html
|
||||
|
||||
to the file you created,
|
||||
|
||||
/~lacser-dovlet/main/~2014.7.30/pub/fab/fun/two/down/hook
|
||||
|
||||
Then we followed a simple rule: you can make the file
|
||||
`/one/two/three` by compiling and running the source file
|
||||
`/one/two/three/hook`.
|
||||
|
||||
/~lacser-dovlet/main/~2014.7.30/pub/fab/fun/two/down
|
||||
|
||||
and then proceeded as above. You can test that this is really a
|
||||
program, and still autoloads and stuff, by doing something funky
|
||||
- like:
|
||||
|
||||
:~ :- %par
|
||||
:~ tex/"This is a "
|
||||
emp/bent/~[tex/"fun experiment "]
|
||||
tex/"in markdown; 2 + 2 is {<(add 2 2)>}."
|
||||
== ==
|
||||
|
||||
|
||||
# Computing an actual function.
|
||||
|
||||
While these files are functions in a sort of abstract sense, they
|
||||
are not actually functions in the normal sense of the word. A
|
||||
function needs arguments, after all.
|
||||
|
||||
Since we're talking to our function through the Web, it makes
|
||||
sense to try to send arguments as query strings. Also, though
|
||||
we've had some fun generating markdown nouns (with totally
|
||||
generic Hoon), it seems more appropriate since we're writing
|
||||
webpages and stuff to generate HTML.
|
||||
|
||||
Let's edit
|
||||
|
||||
~/urb/lacser-dovlet/main/pub/fab/fun/three/hymn.hook
|
||||
|
||||
and put in
|
||||
|
||||
;html
|
||||
;head ;title: Fun Experiment Three
|
||||
==
|
||||
;body
|
||||
;p: This is an ;{i "HTML file."}
|
||||
==
|
||||
==
|
||||
|
||||
and view it at
|
||||
|
||||
https://lacser-dovlet.urbit.org/pub/fab/fun/three
|
||||
|
||||
We saw above how a `%hymn` turns into an `%html`, so we don't
|
||||
need to repeat it. But again, this source file is best seen
|
||||
as a program which produces an HTML tree. Sorta like the DOM.
|
||||
|
||||
(This syntax is also totally generic Hoon. In a sense - Hoon is
|
||||
customized without apology for building XML nouns. In fact, it
|
||||
has special cases for `<script`> and `<style>`, so it's even
|
||||
slightly specific to HTML. This is not the most elegant language
|
||||
design decision in the world, but it seemed preferable to making
|
||||
people use a separate template language. Hoon is not the world's
|
||||
best template language, but it's not just a template language.)
|
||||
|
||||
But what would be nicer would be -
|
||||
|
||||
=+ fib=|=(x=@ ?:((lth x 2) 1 (add $(x (dec x)) $(x (sub x 2)))))
|
||||
;html
|
||||
;head ;title: Fun Experiment Three
|
||||
==
|
||||
;body
|
||||
;p ; This is an ;{i "HTML file"} which
|
||||
; computes the Fibonacci number
|
||||
; of 12: {<(fib 12)>}.
|
||||
==
|
||||
==
|
||||
==
|
||||
|
||||
There's clearly a *function* here. But it's not *of* anything...
|
||||
|
||||
|
||||
# An actual function, query string edition.
|
||||
|
||||
Okay, so we want an actual function. In
|
||||
|
||||
~/urb/lacser-dovlet/pub/fab/fun/four/hymn.hook
|
||||
|
||||
put
|
||||
|
||||
/= gas /$ fuel
|
||||
::
|
||||
=+ arg=(biff (~(get by qix.gas) %number) (slat %ud))
|
||||
=+ fib=|=(x=@ ~+(?:((lth x 2) 1 (add $(x (dec x)) $(x (sub x 2))))))
|
||||
::
|
||||
;html
|
||||
;head
|
||||
;title: Fun Experiment Four
|
||||
==
|
||||
;body
|
||||
;p: Welcome, {<cip.ced.gas>}!
|
||||
;+ ?~ arg
|
||||
;p: Usage: ?number=x
|
||||
;p ; This is an ;{i "HTML file"} which
|
||||
; computes the Fibonacci number
|
||||
; of {<u.arg>}: {<(fib u.arg)>}.
|
||||
==
|
||||
==
|
||||
==
|
||||
|
||||
Try it:
|
||||
|
||||
https://lacser-dovlet.urbit.org/pub/fab/fun/four?number=14
|
||||
https://lacser-dovlet.urbit.org/pub/fab/fun/four?number=144
|
||||
https://lacser-dovlet.urbit.org/pub/fab/fun/four
|
||||
|
||||
This page does appear to be a function of the query string - and
|
||||
some other stuff. For one thing, Urbit is now tracking your
|
||||
behavior. Or at least your IP address.
|
||||
|
||||
Actually, at the Web level, the page broadly speaking is a
|
||||
function of the query string and the session state. The usual
|
||||
Web cruft, basically.
|
||||
|
||||
However, the page is not computed at the chaotic Web level - it's
|
||||
computed at the functional publishing level. Functional
|
||||
publishing does not know the Web exists. We can reveal the true
|
||||
and complete input to the page function with a sneaky trick.
|
||||
|
||||
In
|
||||
|
||||
~/urb/lacser-dovlet/pub/fab/fun/five/hymn.hook
|
||||
|
||||
put
|
||||
|
||||
/= ctx /$ |=([p=beam q=path] +<)
|
||||
::
|
||||
;html
|
||||
;head
|
||||
;title: Fun Experiment Five
|
||||
==
|
||||
;body
|
||||
;p: This page was built at {<(tope p.ctx)>}.
|
||||
;br;
|
||||
;p: The remainder path was {<q.ctx>}.
|
||||
==
|
||||
==
|
||||
|
||||
Try it:
|
||||
|
||||
https://lacser-dovlet.urbit.org/pub/fab/fun/five
|
||||
|
||||
What you'll see is that `q.ctx`, which we used to be passing to
|
||||
the Web path parser `fuel`, is an ugly (but URL-safe) string
|
||||
containing data, encoded as a path component - let's say,
|
||||
`web/DATA`. This string is actually inverted - properly
|
||||
speaking, it's `DATA/web`.
|
||||
|
||||
When we want to compute a dynamic functional namespace which is
|
||||
not just a trivial static file, or a translated file, we divide
|
||||
the Urbit beam into two parts - *driver* and *remainder*.
|
||||
The driver is the path to the function; the remainder is the
|
||||
input to the function.
|
||||
|
||||
In this case, the Urbit webserver asked the filesystem for
|
||||
|
||||
/~lacser-dovlet/main/~2014.7.30/pub/fab/fun/five/DATA/web
|
||||
|
||||
with the content type `%html`. (We lied a little earlier - the
|
||||
content type, or `mark`, is sent separately to the filesystem.)
|
||||
|
||||
To resolve the beam, we travel up it from the bottom, looking for
|
||||
drivers of some sort. Urbit did not find any files in
|
||||
`fun/five/DATA/web`. It also did not find any files in
|
||||
`fun/five/DATA`.
|
||||
|
||||
Going up, it found `fun/five/hymn/hook`, which it took as a way of
|
||||
making `fun/one` with the mark `%hymn`. Ie, an HTML document
|
||||
tree, which we know how to turn into `%html`, so great.
|
||||
|
||||
Then, it gave the remainder of the beam, `/DATA/web` - but
|
||||
inverted to `/web/DATA`, because we decode the remainder from
|
||||
the end inward - as a resource to our page generator. And this
|
||||
is how we could print your IP address and stuff...
|
||||
|
515
main/pub/src/doc/say/arvo/pub3.md
Normal file
515
main/pub/src/doc/say/arvo/pub3.md
Normal file
@ -0,0 +1,515 @@
|
||||
# Ford changes for non-dummies.
|
||||
|
||||
You're used to .hoon files being parsed into a ++twig. This
|
||||
stays true for vanes, hoon.hoon, and %batz apps; but for
|
||||
everything else we get a ++hood.
|
||||
|
||||
It happens that every old .hoon file is also a ++hood. but there
|
||||
are some extra runes at the top which do interesting cool things.
|
||||
|
||||
|
||||
# Top-level file stuff.
|
||||
|
||||
The first, simplest thing that the new build system gives you:
|
||||
without using any other features at all, we treat your file as a
|
||||
list of nested twigs, ie `=~`, not a single twig. So instead of
|
||||
|
||||
=> |%
|
||||
++ library
|
||||
--
|
||||
|%
|
||||
++ program
|
||||
--
|
||||
|
||||
you can write - using my new, official, stylized-tilde divider:
|
||||
|
||||
:: One line that describes this source file.
|
||||
::::
|
||||
:: /hoon/file/fab/pub
|
||||
|%
|
||||
++ library
|
||||
--
|
||||
::
|
||||
::::
|
||||
::
|
||||
|%
|
||||
++ program
|
||||
--
|
||||
|
||||
The conventional way of doing `!:` has also changed. The trouble
|
||||
is that `!:` is a twig and it cannot come above ++hood runes.
|
||||
Actually, the right place to apply !: is per core, and it should
|
||||
stand out visually because it indicates immature code:
|
||||
|
||||
!: |%
|
||||
++ library
|
||||
--
|
||||
|
||||
But you should always use at least one ++hood directive, namely
|
||||
`/?`. This is tied to the version of Urbit as a whole, which in
|
||||
practice means the version of `%zuse` - also defined as ++zuse.
|
||||
Thus, `zuse` is 314 and `hoon` is 164.
|
||||
|
||||
The assumption, currently shaky, is that backward compatibility
|
||||
in both ++zuse and ++hoon will never be broken. This is
|
||||
basically what it means to do Kelvin versioning. With
|
||||
|
||||
/? 314
|
||||
|
||||
you state that this code should work iff `(lte zuse 314)`.
|
||||
|
||||
So this is what the start of a normal .hoon file looks like:
|
||||
|
||||
:: At least one line that describes this source file.
|
||||
::
|
||||
:::: /hoon/file/fab/pub
|
||||
::
|
||||
/? 314
|
||||
/- html, hymn, down, tlon-radio-a
|
||||
/+ tlon-radio, tlon-twitter
|
||||
::
|
||||
:::: ~tasfyn-partyv, ~tomsyt-balsen, ~talsur-todres
|
||||
::
|
||||
|%
|
||||
++ code
|
||||
!!
|
||||
--
|
||||
|
||||
Note that hood runes by convention use *four* spaces, making
|
||||
them visually distinct from twig runes - and preserving the
|
||||
Rastersysteme.
|
||||
|
||||
Formal comments: the path is the `++spur` (path after /===/) of
|
||||
the file, which is generally stored inverted. The list of ships
|
||||
is the list of people who have changed the code, oldest first.
|
||||
|
||||
Anyone whose (new) .hoon files do not match this template is
|
||||
eligible for a yellow card! I mean it!
|
||||
|
||||
|
||||
# Mad hood runes.
|
||||
|
||||
Let's go through these runes one by one, creating a wacky test
|
||||
program as we go.
|
||||
|
||||
There are three main sections of a hood: hooves, horns, hoops.
|
||||
They must be in the file in that order. Let's talk about them
|
||||
in the *opposite* order, though.
|
||||
|
||||
|
||||
# Indirect hoops (simple).
|
||||
|
||||
Hoops are the actual code of your program. You can buy two kinds:
|
||||
|
||||
$% [%& p=twig] :: direct twig
|
||||
[%| p=beam] :: resource location
|
||||
== ::
|
||||
|
||||
The program produced by a hood is a stack of twigs which are
|
||||
composed by `=~`, ie, connected with `=>`. You can specify these
|
||||
twigs directly, as in the above, or you can load them from Urbit.
|
||||
|
||||
Let's build a toy program, `mad`, that uses mad hood skillz.
|
||||
First, let's load it with an indirect hoop. In
|
||||
|
||||
~/urb/pub/src/mad/fib.hoon
|
||||
|
||||
we put
|
||||
|
||||
|%
|
||||
++ fib |=(x=@ ~+(?:((lth x 2) 1 (add $(x (dec x)) $(x (sub x 2))))))
|
||||
--
|
||||
|
||||
and in
|
||||
|
||||
~/urb/pub/fab/mad/one/hymn.hook
|
||||
|
||||
we put
|
||||
|
||||
:: Our first experiment with major hood runes.
|
||||
::
|
||||
:::: /hoon/one/mad/fab/pub
|
||||
::
|
||||
/= gas /$ fuel
|
||||
// /===/pub/src/mad/fib
|
||||
::
|
||||
:::: ~tasfyn-partyv
|
||||
::
|
||||
=+ arg=(biff (~(get by qix.gas) %number) (slat %ud))
|
||||
::
|
||||
;html
|
||||
;head
|
||||
;title: Mad Experiment One
|
||||
==
|
||||
;body
|
||||
;+ ?~ arg
|
||||
;p: Usage: ?number=x
|
||||
;p ; This is an ;{i "HTML file"} which
|
||||
; computes the Fibonacci number
|
||||
; of {<u.arg>}: {<(fib u.arg)>}.
|
||||
==
|
||||
==
|
||||
==
|
||||
|
||||
|
||||
# Indirect hoops (advanced).
|
||||
|
||||
Indirect hoops have a still more amazing power - they can make
|
||||
cores out of directories themselves.
|
||||
|
||||
Let's `cp -r` `fab/mad/one` to `fab/mad/two` and put, in
|
||||
|
||||
~/urb/pub/src/mad/tools/fib.hoon
|
||||
|
||||
the single line
|
||||
|
||||
|=(x=@ ~+(?:((lth x 2) 1 (add $(x (dec x)) $(x (sub x 2))))))
|
||||
|
||||
Then, in
|
||||
|
||||
~/urb/pub/fab/mad/two/hymn.hook
|
||||
|
||||
we put:
|
||||
|
||||
:: Our second experiment with major hood runes.
|
||||
::
|
||||
:::: /hoon/two/mad/fab/pub
|
||||
::
|
||||
/= gas /$ fuel
|
||||
// /===/pub/src/mad/tools
|
||||
::
|
||||
:::: ~tasfyn-partyv
|
||||
::
|
||||
=+ arg=(biff (~(get by qix.gas) %number) (slat %ud))
|
||||
;html
|
||||
;head
|
||||
;title: Mad Experiment Two
|
||||
==
|
||||
;body
|
||||
;+ ?~ arg
|
||||
;p: Usage: ?number=x
|
||||
;p ; This is an ;{i "HTML file"} which
|
||||
; computes the Fibonacci number
|
||||
; of {<u.arg>}: {<(fib u.arg)>}.
|
||||
==
|
||||
==
|
||||
==
|
||||
|
||||
Note that our function name *does not exist anywhere in the
|
||||
source code*, except where it is called. Rather, it's implicit
|
||||
from the filename.
|
||||
|
||||
It would be superfluous to observe that this mechanism is
|
||||
recursive - you can create arbitrarily deep hierarchies of cores
|
||||
whose symbolic namespace follows the source code tree.
|
||||
|
||||
Don't mistake indirect hoops, however, for a library loading
|
||||
mechanism. Hoops may be used to *build* libraries, but `/+`
|
||||
is used to load them. Indirect hoops are for splitting up your
|
||||
program into separate parts, not for sharing code between
|
||||
programs.
|
||||
|
||||
|
||||
# Horns.
|
||||
|
||||
Another thing we like to do is compile data into our application.
|
||||
No, really - one of the stupidest things in many programming
|
||||
environment is dynamically loading static resources. We really
|
||||
try not to do that in Urbit. (At least, this week we try!)
|
||||
|
||||
Before her hoops, the programmer can load a set of referentially
|
||||
transparent `horns`, or resources.
|
||||
|
||||
In
|
||||
~/urb/pub/fab/mad/res/hello/hymn.hook
|
||||
|
||||
we put:
|
||||
|
||||
;p: Hello, world.
|
||||
|
||||
and in
|
||||
|
||||
~/urb/pub/fab/mad/three/hymn.hook
|
||||
|
||||
we put:
|
||||
|
||||
:: This third experiment wears the horns.
|
||||
::
|
||||
:::: /hoon/three/mad/fab/pub
|
||||
::
|
||||
/= hello /: /===/pub/fab/mad/res/hello /hymn/
|
||||
:
|
||||
:::: ~tasfyn-partyv
|
||||
::
|
||||
::
|
||||
;html
|
||||
;head
|
||||
;title: Mad Experiment Three
|
||||
==
|
||||
;body
|
||||
;+ hello
|
||||
==
|
||||
==
|
||||
|
||||
What did we do? We loaded a typed, dynamically built resource
|
||||
from the global namespace onto our reef.
|
||||
|
||||
Note the runic syntax above. You might suspect that this is a
|
||||
tall mode, and you might be right. It condenses:
|
||||
|
||||
/= hello /:/======/res/hello:/hymn/
|
||||
|
||||
Or even:
|
||||
|
||||
Note also how we avoid repeating shared path spans.
|
||||
|
||||
Also, unlike twig runes, hood runes can be replaced with a
|
||||
semantic codename:
|
||||
|
||||
/dub hello /see /======/res/hello /hymn/
|
||||
|
||||
|
||||
# More horns.
|
||||
|
||||
Cool as it is, this example barely scratches the awesome that is
|
||||
Urbit resource loading. For instance, in
|
||||
|
||||
~/urb/pub/fab/mad/res/bible
|
||||
|
||||
put, in `1.html`, `2.html`, and `3.html` respectively,
|
||||
|
||||
<p>The earth was without form, and void.</p>
|
||||
|
||||
<p>Then Cain slew Abel.</p>
|
||||
|
||||
<p>I bring not peace, but a sword.</p>
|
||||
|
||||
then, in
|
||||
|
||||
~/urb/pub/fab/mad/four/hymn.hook
|
||||
|
||||
put
|
||||
|
||||
:: This fourth experiment is profoundly biblical.
|
||||
::
|
||||
:::: /hoon/four/mad/fab/pub
|
||||
::
|
||||
/= bible /: /======/res/bible
|
||||
/; |= a=(list (pair ,@ manx))
|
||||
(turn a |=([* b=manx] b))
|
||||
/@
|
||||
/hymn/
|
||||
::
|
||||
:::: ~tasfyn-partyv
|
||||
::
|
||||
!:
|
||||
;html
|
||||
;head
|
||||
;title: Mad Experiment Four
|
||||
==
|
||||
;body
|
||||
;* bible
|
||||
==
|
||||
==
|
||||
|
||||
What happened here? The `/@` rune loads a list of files named by
|
||||
decimals in ascending order, with the protocol `/hymn/` - the
|
||||
result of parsing HTML. This produces not a list of hymns
|
||||
(actually just `++manx`, the XML node type), but a list of pairs
|
||||
`[number manx]`, so we need to strip the numbers with `/;`.
|
||||
|
||||
We can also understand `/:` a little better. It doesn't mean
|
||||
"load the horn at this beam" - it means "set the beam at which
|
||||
horns within this one are loaded." There is also its cousin
|
||||
`/,`, which just descends within the current beam. The beam
|
||||
starts as the path to the hook file itself.
|
||||
|
||||
Nothing about this system restricts the tree to a fixed depth -
|
||||
we could have not just `/hymn/` in our bible directory,
|
||||
but another `/@` or other arbitrary resource directives.
|
||||
|
||||
`/@` has several cousins for different naming schemes. `/|`
|
||||
reads a folder named by `@dr`, ie, relative date. `/&` reads
|
||||
one named by `@da`, ie, absolute date. An excellent way to
|
||||
organize random events named only by time is to nest `/|` within
|
||||
`/&`, eg, a tree of days with that day's events in it.
|
||||
|
||||
You can also use `/%` to load a map of unparsed names. Finally,
|
||||
`/*` handles multiple heterogeneous subtrees with different
|
||||
horns. You can cast the product of `/*` to a map with `/^`,
|
||||
or you can leave it as a tuple. You can also build a simple list
|
||||
of horns with `/.`.
|
||||
|
||||
Finally, we've already seen `/$`, which calls a parsing function
|
||||
(`++fuel` for web remainders) on the location and remainder of
|
||||
the file. And `/~` lets you stick a simple twig directly in your
|
||||
resource tree, for computing "resources" entirely by hand.
|
||||
|
||||
Here is the structure of horns:
|
||||
|
||||
++ horn :: resource tree
|
||||
$% [%ape p=twig] :: /~ twig by hand
|
||||
[%arg p=twig] :: /$ argument
|
||||
[%day p=horn] :: /| list by @dr
|
||||
[%dub p=term q=horn] :: /= apply face
|
||||
[%fan p=(list horn)] :: /. list
|
||||
[%for p=path q=horn] :: /, descend
|
||||
[%hub p=horn] :: /@ list by @ud
|
||||
[%man p=(map span horn)] :: /* hetero map
|
||||
[%nap p=horn] :: /% homo map
|
||||
[%now p=horn] :: /& list by @da
|
||||
[%saw p=twig q=horn] :: /; operate on
|
||||
[%see p=beam q=horn] :: /: relative to
|
||||
[%sic p=tile q=horn] :: /^ cast
|
||||
[%toy p=mark] :: /mark/ static
|
||||
== ::
|
||||
|
||||
See `%ford` for the structure of horns, and `++fair` in ford for
|
||||
the exact syntax and semantics.
|
||||
|
||||
|
||||
# Hooves: structures.
|
||||
|
||||
At the top of your hood comes the proper way of sharing code
|
||||
between programs: `/-` and `/+`, for structure and library
|
||||
loading respectively.
|
||||
|
||||
Let's try `/-` by reusing one of our earlier examples - this
|
||||
time, with an actual type cast. In
|
||||
|
||||
~/urb/pub/fab/mad/five/down.hook
|
||||
|
||||
put
|
||||
|
||||
/- down
|
||||
^- down
|
||||
:~ :- %par
|
||||
:~ tex/"This is a "
|
||||
emp/bent/~[tex/"fun experiment "]
|
||||
tex/"in markdown; 2 + 2 is {<(add 2 2)>}."
|
||||
== ==
|
||||
|
||||
In plain English, we loaded the `++down` structure and cast our
|
||||
hand-rolled markdown to it. Where did we get this code, exactly?
|
||||
It came from `/===/sur/down/gate/hook`:
|
||||
|
||||
/? 314
|
||||
/- *markdown
|
||||
down
|
||||
|
||||
The `*` tells us that we are dealing not with a single gate, but
|
||||
a core containing multiple gates. `/===/sur/markdown/core/hook`:
|
||||
|
||||
|%
|
||||
++ down (list barb) :: markdown structure
|
||||
++ barb :: block elements
|
||||
$% [%had p=@ud q=(list shin) r=(unit tape)] :: depth, contents, id
|
||||
[%hem p=manx] :: html tag
|
||||
[%hot ~] :: horizontal rule
|
||||
[%lie p=down] :: list element
|
||||
[%lit p=? q=down] :: list
|
||||
[%par p=(list shin)] :: paragraph
|
||||
[%pre p=wall] :: preformatted text
|
||||
[%quo p=down] :: blockquote
|
||||
== ::
|
||||
++ shin :: span elements
|
||||
$% [%cod p=tape] :: inline code
|
||||
[%cut ~] :: break
|
||||
[%emp p=?(%bent %bold %both) q=(list shin)] :: emphasis
|
||||
[%ike p=(list shin)] :: strikethrough
|
||||
[%lin p=(list shin) q=tape r=(unit tape)] :: link
|
||||
[%tex p=tape] :: text
|
||||
== ::
|
||||
--
|
||||
|
||||
We see that markdown isn't a trivial single structure. With `*`
|
||||
in a hoof we do two things. One, we load a core not a gate.
|
||||
Two, we implicitly stick an
|
||||
|
||||
=+ markdown
|
||||
|
||||
on your reef, so that you can use `down` where otherwise you'd
|
||||
have to say `down:markdown`. `down/gate/hook` uses this to
|
||||
export the naked `down` gate into the structure space.
|
||||
|
||||
What actually happens to all the resources you load with `/-`?
|
||||
They go into a single core above the libraries, horns and hoops.
|
||||
Moreover, when libraries, horns and hoops contain `/-` runes of
|
||||
their own, indicating structure dependencies, *all* these
|
||||
dependencies are collected into the single structure core.
|
||||
|
||||
|
||||
# Hooves: global names and conflicts.
|
||||
|
||||
When we use a single symbol, like `down`, we indicate that we're
|
||||
looking for a structure in the same ship, desk, and case as our
|
||||
own. In other words, we're loading a local structure which is
|
||||
tightly coupled to the system loading it.
|
||||
|
||||
We can also use global structures. Here the syntax is
|
||||
|
||||
name/label/~ship
|
||||
|
||||
The desk in the global case is bound to `%main`, because it's
|
||||
assumed that if you're publishing your code to the world it
|
||||
belongs on your `%main`.
|
||||
|
||||
But this still becomes an arm named `structure` in your structure
|
||||
core. What happens if we depend in two places on two hooves,
|
||||
with the same name but different labels or ships?
|
||||
|
||||
For now, we are conservative - all hooves in a hood must match
|
||||
exactly. Eventually we'll parse conventional labels (for Kelvin
|
||||
and semantic versioning) and perform automagic upgrades.
|
||||
|
||||
|
||||
# Hooves: libraries.
|
||||
|
||||
Libraries are loaded much the same way as structures, except with
|
||||
`/+`. The syntax is the same, except that there is no equivalent
|
||||
of `*`. Library dependencies are also collected across the
|
||||
entire build, like structure dependencies.
|
||||
|
||||
Libraries are assumed to be cores, and they are stacked one after
|
||||
another in dependency order - if a depends on b, a obviously must
|
||||
come after b.
|
||||
|
||||
Let's try using a library. In
|
||||
|
||||
~/urb/lib/example/core.hook
|
||||
|
||||
put
|
||||
|
||||
|%
|
||||
++ fib |=(x=@ ~+(?:((lth x 2) 1 (add $(x (dec x)) $(x (sub x 2))))))
|
||||
--
|
||||
|
||||
Then, in
|
||||
|
||||
~/urb/pub/fab/mad/six/hymn.hook
|
||||
|
||||
we put:
|
||||
:: Our sixth experiment with major hood runes.
|
||||
::
|
||||
:::: /hoon/six/mad/fab/pub
|
||||
::
|
||||
/+ example
|
||||
/= gas /$ fuel
|
||||
::
|
||||
:::: ~tasfyn-partyv
|
||||
::
|
||||
=+ arg=(biff (~(get by qix.gas) %number) (slat %ud))
|
||||
;html
|
||||
;head
|
||||
;title: Mad Experiment Two
|
||||
==
|
||||
;body
|
||||
;+ ?~ arg
|
||||
;p: Usage: ?number=x
|
||||
;p ; This is an ;{i "HTML file"} which
|
||||
; computes the Fibonacci number
|
||||
; of {<u.arg>}: {<(fib u.arg)>}.
|
||||
==
|
||||
==
|
||||
==
|
||||
|
29
main/pub/src/doc/say/intro.md
Normal file
29
main/pub/src/doc/say/intro.md
Normal file
@ -0,0 +1,29 @@
|
||||
#Urbit
|
||||
|
||||
We got tired of system software from the 1970s. So we wrote our own. From scratch.
|
||||
|
||||
##Nock, a minimal virtual machine
|
||||
|
||||
[Nock](https://github.com/urbit/urbit/blob/master/urb/zod/spec/nock/5.txt) is a
|
||||
homoiconic combinator algebra, not much fancier than SKI combinators. The spec
|
||||
fits on a T-shirt and gzips to 340 bytes. We never extend Nock or call out to Unix from it.
|
||||
|
||||
##Hoon, a typed functional language
|
||||
|
||||
Hoon is a strict, typed, functional language that compiles itself to Nock.
|
||||
As a functional systems language, Hoon is especially good at metaprogramming,
|
||||
self-virtualization, hotpatching; marshalling and validating untyped data;
|
||||
decoding and encoding binary message formats. Hoon is designed for event
|
||||
programming, so there is no concurrency model.
|
||||
|
||||
##Arvo, a functional operating system
|
||||
|
||||
Arvo is an event-driven server OS built on the same event library as node.js
|
||||
(libuv). Unlike node.js, Arvo is written in Hoon, isolated from Unix and a
|
||||
persistent single-level store. Arvo is modular. Present modules provide a
|
||||
network messaging protocol, a REPL and task manager, a revision-controlled
|
||||
filesystem, a text console, and an HTTP server.
|
||||
|
||||
|
||||
|
||||
|
463
main/pub/src/doc/say/setup.md
Normal file
463
main/pub/src/doc/say/setup.md
Normal file
@ -0,0 +1,463 @@
|
||||
#Setup
|
||||
|
||||
> Tlön is surely a labyrinth, but it is a labyrinth devised
|
||||
> by men, a labyrinth destined to be deciphered by men.
|
||||
> - Tlön, Uqbar, Orbis Tertius
|
||||
|
||||
Urbit runs on Unix machines only. It depends on:
|
||||
|
||||
+ gmp
|
||||
+ libsigsegv
|
||||
+ openssl
|
||||
+ libssl-dev (Linux only)
|
||||
+ ncurses (Linux only)
|
||||
|
||||
Currently we support OSX, Linux (not all distributions have been
|
||||
tested) and \*BSD. There are no instructions for BSD, because
|
||||
only people with a serious clue run BSD. Intrepid ninjas may
|
||||
attempt ports to other OSes. If you're not an intrepid ninja,
|
||||
try a VM (eg, VirtualBox) or use one of our AMIs.
|
||||
|
||||
###Amazon AMIs
|
||||
|
||||
There are public AMIs at the following locations:
|
||||
|
||||
us-west (oregon) ami-6cf88d5c
|
||||
us-west (n. california) ami-78d4ec3d
|
||||
us-east (n. virginia) ami-cd819ba4
|
||||
|
||||
These use Debian Wheezy, so you should be able to
|
||||
|
||||
ssh -i /path/to/your/key/file.pem admin@123.123.123.123
|
||||
|
||||
to get in.
|
||||
|
||||
Since the codebase is changing frequently, run the following once you have ssh-ed in.
|
||||
|
||||
cd /urbit/
|
||||
git pull origin master
|
||||
make clean; make;
|
||||
|
||||
Then jump to "Run" below to get rolling.
|
||||
|
||||
|
||||
###Docker
|
||||
|
||||
[Docker](http://docker.io) is a very convenient way to get in to an Urbit ship quickly. `~simmev-rabryd` maintains the docker approach on GitHub [here](https://github.com/yebyen/urbinit).
|
||||
|
||||
Follow the instructions on the GitHub page, then proceed to "Run" below.
|
||||
|
||||
|
||||
###OS X###
|
||||
|
||||
1. Do you have XCode? Type `gcc`. If it says `no input files`, you have XCode.
|
||||
|
||||
Otherwise, install XCode: `https://developer.apple.com/xcode/`, with the
|
||||
command line tools.
|
||||
|
||||
2. Install dependencies. Pick either one of Homebrew or Macports, but not both.
|
||||
|
||||
- Homebrew? Type `brew`. If it does something, you have Homebrew.
|
||||
|
||||
Otherwise, `ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"`
|
||||
will install it.
|
||||
|
||||
And follow up with `brew install git gmp libsigsegv openssl`
|
||||
|
||||
This will ask you for the root password, which ideally you know.
|
||||
|
||||
- Macports? Type `port`. If it does something, you have Macports.
|
||||
|
||||
Otherwise go [here](http://www.macports.org/install.php "here").
|
||||
|
||||
Then `sudo port install git gmp libsigsegv openssl`
|
||||
|
||||
Enter your root password at the prompt.
|
||||
|
||||
|
||||
###Linux (Ubuntu or Debian)###
|
||||
|
||||
1. `sudo apt-get install libgmp3-dev libsigsegv-dev openssl libssl-dev libncurses5-dev git make exuberant-ctags`
|
||||
|
||||
|
||||
###Linux (AWS)###
|
||||
|
||||
1. `sudo yum --enablerepo epel install gcc git gmp-devel openssl-devel ncurses-devel libsigsegv-devel ctags`
|
||||
|
||||
###Get the source###
|
||||
|
||||
Urbit uses git for its revision control. Eventually, Urbit will use itself, but for now,
|
||||
you need git. If you've followed the above instructions correctly then typing `git` in your terminal should do something.
|
||||
|
||||
If that works, run:
|
||||
|
||||
git clone https://github.com/urbit/urbit.git
|
||||
|
||||
to download Urbit from its repository.
|
||||
|
||||
If for some reason you have moral qualms about using Git, you can also just download and unzip `https://github.com/urbit/urbit/archive/master.zip`. This won't provide any version control
|
||||
|
||||
###Set up your enviroment###
|
||||
|
||||
`cd` to the unpacked Urbit directory you just created:
|
||||
|
||||
cd urbit
|
||||
|
||||
If this works, `ls urb` should show:
|
||||
|
||||
urbit.pill zod/
|
||||
|
||||
Great! Now, let's do some dirty Unix stuff to set up your environment.
|
||||
If you know what this is doing, feel free to do it right. Otherwise:
|
||||
|
||||
echo "export URBIT_HOME=`pwd`/urb" >>~/.bash_profile
|
||||
source ~/.bash_profile
|
||||
|
||||
To make sure this worked,
|
||||
|
||||
echo $URBIT_HOME
|
||||
|
||||
should show `/urb` within the current directory.
|
||||
|
||||
If this didn't work, you'll have to do this the hard way. run `vi ~/.bash_profile` and fix it.
|
||||
|
||||
###Build###
|
||||
|
||||
`make`. Sometimes things are just easy.
|
||||
|
||||
<h3 id="run">Run</h3>
|
||||
|
||||
Run `bin/vere -c mypier`, where `mypier` is a directory that doesn't yet exist.
|
||||
All your state (an append-only log and a memory checkpoint) will live in this
|
||||
directory. Its name doesn't matter and is not visible internally.
|
||||
|
||||
A _pier_ is an Urbit virtual machine that hosts one or more Urbit identities,
|
||||
or _ships_. When you run `vere -c`, it automatically creates a 128-bit ship,
|
||||
or `submarine`. Your name (a hash of a randomly-generated public key) will
|
||||
look like:
|
||||
|
||||
~machec-binnev-dordeb-sogduc--dosmul-sarrum-faplec-nidted
|
||||
|
||||
First you'll see a string of messages like:
|
||||
|
||||
vere: urbit home is /Users/cyarvin/Documents/src/u3/urb
|
||||
loom: mapped 1024MB
|
||||
time: ~2013.9.1..03.57.11..4935
|
||||
ames: on localhost, UDP 63908.
|
||||
generating 2048-bit RSA pair...
|
||||
|
||||
and then it'll pause a little, 'cause this is slow, and then
|
||||
|
||||
saving passcode in /Users/cyarvin/.urbit/~magsut-hopful.txt
|
||||
(for real security, write it down and delete the file...)
|
||||
|
||||
and, then, if the network gods are happy, your submarine will start pulling
|
||||
down Arvo files:
|
||||
|
||||
+ /~machec-binnev-dordeb-sogduc--dosmul-sarrum-faplec-nidted/main/1/bin/ticket/hoon
|
||||
+ /~machec-binnev-dordeb-sogduc--dosmul-sarrum-faplec-nidted/main/1/bin/reset/hoon
|
||||
+ /~machec-binnev-dordeb-sogduc--dosmul-sarrum-faplec-nidted/main/1/bin/ye/hoon
|
||||
+ /~machec-binnev-dordeb-sogduc--dosmul-sarrum-faplec-nidted/main/1/bin/ls/hoon
|
||||
|
||||
and the like. You'll see a couple pages of this stuff. Don't worry too much
|
||||
about the details right now. Finally, you'll get the Arvo shell prompt (which
|
||||
is also a Hoon REPL):
|
||||
|
||||
~machec-binnev-dordeb-sogduc--dosmul-sarrum-faplec-nidted/try=>
|
||||
|
||||
###Register###
|
||||
|
||||
Next, you need to decide whether a mere submarine is enough for
|
||||
you right now. This monicker is a mouthful. You can stick with
|
||||
it (for now), but you're going to need a wider xterm.
|
||||
|
||||
Which might be fine! However, please note that just by sending a
|
||||
simple email, you can get a much better ship - a `destroyer`,
|
||||
with a nice short name like
|
||||
|
||||
~waclux-tomwyc
|
||||
|
||||
Just email `urbit@urbit.org`, with your submarine in the subject.
|
||||
We'll send you destroyers - not one, but _two_. Yes, two! Tell
|
||||
us something cool in the body, and we'll send you even more.
|
||||
|
||||
If you have a destroyer, you need to configure it. Otherwise,
|
||||
just stretch that xterm wide and skip to section 1.2.
|
||||
|
||||
Your destroyers will arrive in the form of `[ship ticket]` pairs.
|
||||
Let's say one of your ships is `~waclux-tomwyc` and its ticket is
|
||||
|
||||
~ribdyr-famtem-larrun-figtyd
|
||||
|
||||
(What are these strings, anyway? Just random unsigned integers,
|
||||
rendered in Hoon's syllabic base, `@p`.)
|
||||
|
||||
A new life awaits you on the off-world colonies! To begin, just
|
||||
type at the prompt:
|
||||
|
||||
:begin ~waclux-tomwyc
|
||||
|
||||
and follow the directions. When the script completes, hit return
|
||||
and you'll be the `~waclux-tomwyc` you wanted to be.
|
||||
|
||||
##Play with Arvo##
|
||||
|
||||
If all went well, you now have a nice short prompt:
|
||||
|
||||
~waclux-tomwyc/try=>
|
||||
|
||||
If all did not go well (send us another email), or you're just
|
||||
too impatient to wait for your destroyer, you have a big long
|
||||
prompt. Which is fine, really, just ugly - and all these
|
||||
exercises will still work.
|
||||
|
||||
###Example commands###
|
||||
|
||||
Let's try a few quick things to stretch your fingers. Type these
|
||||
command lines and you should see the matching results:
|
||||
|
||||
~waclux-tomwyc/try=> "hello, world"
|
||||
"hello, world"
|
||||
|
||||
~waclux-tomwyc/try=> (add 2 2)
|
||||
4
|
||||
|
||||
~waclux-tomwyc/try=> :hello %world
|
||||
"hello, world."
|
||||
|
||||
~waclux-tomwyc/try=> :cat /=main=/bin/hello/hoon
|
||||
::
|
||||
:: /=main=/bin/hello/hoon
|
||||
::
|
||||
|= *
|
||||
|= [planet=@ta ~]
|
||||
^- bowl
|
||||
:_ ~ :_ ~
|
||||
:- %%
|
||||
!>("hello, {(trip planet)}.")
|
||||
|
||||
What did you just do?
|
||||
|
||||
One, you used Arvo as a Hoon REPL to print the constant `"hello,
|
||||
world"`, which is a fancy way to write the Nock noun
|
||||
|
||||
[104 101 108 108 111 44 32 119 111 114 108 100 0]
|
||||
|
||||
Two, you called the Hoon `add` function to see that two plus two
|
||||
is four. Math seems to work the same on the off-world colonies.
|
||||
|
||||
Three, you ran the Arvo application `:hello` with the argument
|
||||
`%world`, which is just a fancy way to write the atom
|
||||
`431.316.168.567` (or, for non-Germans, `431,316,168,567`). You
|
||||
might recognize it better as `0x64.6c72.6f77` - the ASCII
|
||||
characters in LSB first order.
|
||||
|
||||
(Is Urbit German? Sadly, no. But all our noun print formats are
|
||||
URL-safe, which dot is and comma isn't.)
|
||||
|
||||
And you (4) used the Arvo application :cat to print the Hoon file
|
||||
|
||||
/=main=/bin/hello/hoon
|
||||
|
||||
which, supposing your current date is
|
||||
|
||||
~2013.9.1..04.38.31..f259
|
||||
|
||||
(ie, September 1, 2013 at 4:38:31 GMT/LS25 plus 0xf259/65536
|
||||
seconds), is equivalent to the global path
|
||||
|
||||
/~waclux-tomwyc/main/~2013.8.23..04.38.31..f259/bin/hello/hoon
|
||||
|
||||
which anyone in Urbit can, see and even use - but we're getting
|
||||
ahead of ourselves.
|
||||
|
||||
###Control characters###
|
||||
|
||||
In any case, what we've seen is that Arvo is a dangerous and
|
||||
powerful operating system which if handled improperly can cause
|
||||
serious injury or loss of life. We exaggerate. Slightly.
|
||||
|
||||
The first thing you need to know is how to control this tool.
|
||||
Try your arrow keys - you'll see that Arvo has traditional Unix
|
||||
history editing. Up and down, left and right work, as do the
|
||||
simple emacs controls:
|
||||
|
||||
^A go to beginning of line
|
||||
^B left arrow
|
||||
^D delete next character
|
||||
^E go to end of line
|
||||
^F right arrow
|
||||
^K kill to end of line
|
||||
^L clear the screen
|
||||
^R search through history
|
||||
^U kill the whole line
|
||||
^Y yank (restore from kill ring)
|
||||
|
||||
Don't expect any other emacs (or even readline - this is not readline, it's
|
||||
internal to Arvo) commands to work.
|
||||
|
||||
There are also some special control keys specific to Arvo. It's
|
||||
a good idea to learn these first so that you feel in, um,
|
||||
control.
|
||||
|
||||
First, we'll quit out of an infinite loop with `^C`:
|
||||
|
||||
~waclux-tomwyc/try=> :infinite
|
||||
|
||||
When you hit return at the end of this line, Arvo will appear to
|
||||
hang. Do not be alarmed! This is not a bug - it means that
|
||||
we've started running our infinite loop before printing the next
|
||||
console prompt. Simply hit `^C`, and you'll see
|
||||
|
||||
! intr
|
||||
~waclux-tomwyc/try=> :infinite
|
||||
|
||||
(There may be some stacktrace stuff before the `! intr`, depending
|
||||
on whether your kernel was compiled with debugging.)
|
||||
|
||||
Hit `^U` to delete the line and escape from infinity. Arvo is a
|
||||
deterministic OS; you interrupted it while processing an event
|
||||
that would never terminate. It returns to the state it was in
|
||||
before you hit return - as if nothing had ever happened.
|
||||
|
||||
You're probably used to using nondeterministic, preemptive OSes,
|
||||
in which the difference between a waiting task and an
|
||||
executing event isn't apparent to the user. Since Arvo is not
|
||||
preemptive, it has two very different states: waiting and
|
||||
working.
|
||||
|
||||
When Arvo is working, `^C` cancels the event it's working on.
|
||||
This event never happened. Don't worry, nothing bad will happen
|
||||
to your computer.
|
||||
|
||||
When Arvo is waiting, use `^D` to end the current task, which is
|
||||
the task that's currently prompting you. If there is a live
|
||||
prompt and the cursor is not at the end, `^D` will delete the
|
||||
current character - as in Unix.
|
||||
|
||||
Try this by running
|
||||
|
||||
~waclux-tomwyc/try=> :begin
|
||||
|
||||
Do you have a ship and a ticket? yes
|
||||
|
||||
Then hit `^D` and you'll be back to the command prompt (which,
|
||||
unlike in Unix, is not a task itself, but part of the OS).
|
||||
|
||||
We don't always want to kill the prompting task. We often want
|
||||
to switch between tasks, or between tasks and the command line.
|
||||
Sort of like switching between windows, except in a command line.
|
||||
We do this with `^X`. Try
|
||||
|
||||
~waclux-tomwyc/try=> :begin
|
||||
|
||||
Do you have a ship and a ticket? yes
|
||||
|
||||
But hit `^X` instead of `^D`. You'll get a prompt again. Use
|
||||
it:
|
||||
|
||||
~waclux-tomwyc/try=> :begin
|
||||
|
||||
~waclux-tomwyc/try=> :hello %world
|
||||
"hello, world."
|
||||
~waclux-tomwyc/try=>
|
||||
|
||||
Hit `^X` again:
|
||||
|
||||
~waclux-tomwyc/try=> :begin
|
||||
|
||||
~waclux-tomwyc/try=> :hello %world
|
||||
"hello, world."
|
||||
Do you have a ship and a ticket? yes
|
||||
|
||||
And finally, hit `^C` to kill the task.
|
||||
|
||||
Lastly, Arvo is a single-level store. Since it's not the '70s
|
||||
anymore and disk is cheap, everything you do is saved for ever.
|
||||
(In fact, it's saved in two ways - as a memory image and an event
|
||||
log - so you, or the government if they haz your filez, can
|
||||
repeat every computation you've ever performed.)
|
||||
|
||||
If the current prompt is just the shell prompt, `^D` on an empty
|
||||
line will log out - as in Unix:
|
||||
|
||||
~waclux-tomwyc/try=>
|
||||
oxford:~/urbit; pwd
|
||||
/Users/cyarvin/urbit
|
||||
oxford:~/urbit; echo "hello, world"
|
||||
hello, world
|
||||
oxford:~/urbit;
|
||||
|
||||
Then you can restart and be right back where you were - just
|
||||
run `vere` without `-c`:
|
||||
|
||||
oxford:~/urbit; bin/vere mypier
|
||||
vere: urbit home is /Users/cyarvin/urb
|
||||
loom: loaded 9MB
|
||||
time: ~2013.9.1..17.23.05..0cc1
|
||||
ames: on localhost, UDP 60342.
|
||||
http: live on 8080
|
||||
rest: checkpoint to event 383
|
||||
rest: old 0v1c.gkr1o, new 0v10.m4gdu
|
||||
---------------- playback complete----------------
|
||||
waclux-tomwyc/try=>
|
||||
|
||||
Use your arrow keys and you'll see your history is still there.
|
||||
Arvo is indestructible and can be shut down however you like
|
||||
without losing data. Also, starting a new task while an old
|
||||
one is still running will kill the old one safely.
|
||||
|
||||
But don't try to operate the same ship on two Unix hosts at the
|
||||
same time. This will confuse everyone, including yourself.
|
||||
|
||||
###System administration###
|
||||
|
||||
Sometimes we make changes to Hoon or Arvo (we never make changes
|
||||
to Nock) and you need to update your ship.
|
||||
|
||||
There are two steps to updating. You need to get the new files,
|
||||
and you need to install them. To get them:
|
||||
|
||||
~waclux-tomwyc/try=> :update
|
||||
: /~waclux-tomwyc/arvo/2/hoon/hoon
|
||||
: /~waclux-tomwyc/arvo/2/dill/hoon
|
||||
: /~waclux-tomwyc/arvo/2/batz/hoon
|
||||
|
||||
To install them (the simplest, slowest, most general way):
|
||||
|
||||
~waclux-tomwyc/try=> :reset
|
||||
|
||||
%reset-start
|
||||
%reset-parsed
|
||||
%reset-compiled
|
||||
%hoon-load
|
||||
[%tang /~waclux-tomwyc/arvo/~2013.11.26..20.29.15..090f/zuse ~tirnux-latwex]
|
||||
[%vane %a /~waclux-tomwyc/arvo/~2013.11.26..20.29.15..090f/ames ~tolryn-watret]
|
||||
[%vane %b /~waclux-tomwyc/arvo/~2013.11.26..20.29.15..090f/batz ~donfex-ladsem]
|
||||
[%vane %c /~waclux-tomwyc/arvo/~2013.11.26..20.29.15..090f/clay ~picsug-mitref]
|
||||
[%vane %d /~waclux-tomwyc/arvo/~2013.11.26..20.29.15..090f/dill ~dilpex-laptug]
|
||||
[%vane %e /~waclux-tomwyc/arvo/~2013.11.26..20.29.15..090f/eyre ~forbur-disben]
|
||||
|
||||
All of your state, including running tasks, will be unchanged.
|
||||
|
||||
Sometimes the interpreter, called `vere` gets updated. In your urbit directory, back in Unixland, run:
|
||||
|
||||
git pull origin master
|
||||
|
||||
every so often to get the latest Urbit source code.
|
||||
|
||||
###Chat###
|
||||
|
||||
Okay, fine. You're a long way from being an Arvo ninja. But -
|
||||
you're ready for the two most important uses of Urbit right now.
|
||||
One, coding. Two, chatting.
|
||||
|
||||
To start coding, read the next chapter. To start chatting,
|
||||
simply type
|
||||
|
||||
~waclux-tomwyc/try=> :chat
|
||||
&
|
||||
|
||||
and type `?` for help.
|
||||
|
||||
[On to the documentation.](/doc/)
|
Loading…
Reference in New Issue
Block a user