more sitework

This commit is contained in:
johncburnham 2014-08-08 20:55:42 -07:00
parent be038f4c11
commit 438732a670
12 changed files with 4565 additions and 8 deletions

View File

@ -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

View 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;
}
}

View 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
==
==
==
==

View 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
==
==
==
==

View 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
==
==
==
==

View 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
==
==
==
==

View 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

File diff suppressed because it is too large Load Diff

View 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...

View 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)>}.
==
==
==

View 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.

View 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/)