on master help sign in
merge master and style fixes
4296dc2Morgan Sutherland
README.md+2−2
@@ -7,10 +7,10 @@ The website for the Urbit project.
7## Build
7## Build
8
8
91. git clone this repository
91. git clone this repository
10−2. Install [zola@0.5.1](https://github.com/getzola/zola/releases/tag/v0.5.1)
10+2. Install [zola@0.8.0](https://github.com/getzola/zola/releases/tag/v0.8.0) (see [Installation](https://www.getzola.org/documentation/getting-started/installation/))
113. `git submodule init`
113. `git submodule init`
124. `git submodule update`
124. `git submodule update`
13−5. In the root directory of `urbit.org`, run `zola build` or `zola serve`
13+5. In the root directory of `urbit.org`, run `zola serve`
14
14
15## Deploy
15## Deploy
16
16
config.toml+3−2
@@ -42,9 +42,10 @@ urbit_menu = [
42]
42]
43
43
44urbit_footer = [
44urbit_footer = [
45− {url = "$BASE_URL/privacy/", name = "Privacy Policy"},
45+ {url = "$BASE_URL/faq/", name = "FAQ"},
46− {url = "$BASE_URL/tos/", name = "Terms of Service"},
47 {url = "$BASE_URL/bounty/", name = "Bug Bounty + Security"},
46 {url = "$BASE_URL/bounty/", name = "Bug Bounty + Security"},
47+ {url = "$BASE_URL/tos/", name = "Terms of Service"},
48+ {url = "$BASE_URL/privacy/", name = "Privacy Policy"},
48]
49]
49
50
50docs_number_chapters = true
51docs_number_chapters = true
content/faq.mdnew file+360
@@ -0,0 +1,360 @@
1++++
2+title = "FAQ"
3++++
4+
5+## Contents
6+
7+<details><summary>The Urbit Project</summary>
8+
9+- [Why Urbit?](#urbit-project-1)
10+- [How secure is Urbit right now?](#urbit-project-2)
11+- [How can I contribute to Urbit?](#urbit-project-3)
12+- [What is Tlon?](#urbit-project-4)
13+- [Who Works at Tlon?](#urbit-project-5)
14+
15+</details>
16+
17+
18+<details><summary>Azimuth</summary>
19+
20+- [What is Azimuth?](#azimuth-1)
21+- [What are stars, galaxies, and planets?](#azimuth-2)
22+- [How many planets, stars, and galaxies are active?](#azimuth-3)
23+- [What are comets and moons?](#azimuth-4)
24+- [What is a `@p`? Why is my username generated for me?](#azimuth-5)
25+- [Can I change my `@p`?](#azimuth-6)
26+- [How do I get an identity?](#azimuth-7)
27+- [How do I send an identity to someone else?](#azimuth-8)
28+- [What is the best way to access Azimuth?](#azimuth-9)
29+
30+</details>
31+
32+<details><summary>Urbit HD Wallet</summary>
33+
34+- [What is the Urbit HD Wallet?](#wallet-1)
35+- [What is a master ticket?](#wallet-2)
36+- [What is an ownership address?](#wallet-3)
37+- [What are proxies?](#wallet-4)
38+- [What are seeds?](#wallet-5)
39+- [What does it mean to “set public keys”?](#wallet-6)
40+- [What do I do if I want to own multiple points?](#wallet-7)
41+- [How should I take care of my Urbit HD Wallet?](#wallet-8)
42+- [I have a galaxy or star with lockup conditions. How does this work?](#wallet-9)
43+
44+</details>
45+
46+<details><summary>Arvo</summary>
47+
48+- [What is Arvo?](#arvo-1)
49+- [What is unique about Arvo?](#arvo-2)
50+- [How is Arvo connected to Ethereum?](#arvo-3)
51+- [Will the Arvo network survive if Ethereum dies?](#arvo-4)
52+- [How do I install Arvo?](#arvo-5)
53+- [How do I use Arvo?](#arvo-6)
54+- [How do over-the-air updates work?](#arvo-7)
55+- [What is Landscape?](#arvo-8)
56+- [I have a point, now what?](#arvo-9)
57+
58+</details>
59+
60+<details><summary>Grants</summary>
61+
62+- [What are the different types of grants](#grants-1)
63+- [How do I get a gift?](#grants-2)
64+- [How often are gifts awarded?](#grants-3)
65+- [What happens if a user does not complete a proposal but meets some milestones?](#grants-4)
66+- [Do I have to pay taxes on stars?](#grants-5)
67+- [Can I create a bounty?](#grants-6)
68+- [How are proposals approved?](#grants-7)
69+- [If I win a proposal, do I have an exclusive claim on the project?](#grants-8)
70+- [Will milestones be embedded in a smart contract?](#grants-9)
71+- [Does reputation play a role in the assessment of grants?](#grants-10)
72+- [I completed a proposal. When will I receive payment?](#grants-11)
73+- [Can I see the proposals of others that have been accepted?](#grants-12)
74+- [How do I safely store my stars?](#grants-13)
75+- [Can I delegate a bounty or proposal to someone else?](#grants-14)
76+
77+</details>
78+
79+## The Urbit project
80+
81+### Why Urbit? {#urbit-project-1}
82+
83+The internet began as a friendly, peer-to-peer network: friendly because the people with the ability to use such a thing were the same sort of nerds, and peer-to-peer because those nerds knew how to run their own servers. But, as the internet’s appeal spread to everyday people in the 90s, it was unrealistic for everyone to be the administrators of their own complex machines. So this peer-to-peer network was divided into clients (people) and servers (large corporations), a relationship that’s been solidifying ever since.
84+
85+But one intervening development presents hope. Platforms like Amazon Web Services turn previously heterogeneous networking infrastructure into commodities. Now anyone can use it almost as easily as a faucet.
86+
87+What’s missing, however, is a peer-to-peer software stack that everyone can use. That’s what Urbit is. We’re reimagining the internet as a peer-to-peer network that is designed to stay that way, even at a large scale, which is something that’s never been done before. All previous attempts to re-decentralize the internet have only attempted to solve certain corners of the problem, and thus have failed. Only a technical upheaval of the entire stack can bring back this dream. Urbit does just that, and already exists in a working state.
88+
89+In combining the determinism of Arvo, our OS, and the security of identity of Azimuth, our identity layer, we’ve done something that’s never been done before: we’ve created an encrypted peer-to-peer network where you can be sure that your message is secure: not tampered with, and sent exactly once.
90+
91+The internet anticipated by Urbit is a much friendlier one. You’re always “logged in” to your online services, without the need to remember usernames or passwords. Problems that are unsolvable in our current internet -- spam, fake reviews, malware-spreading, harassment -- are made uneconomical, since there’s a limited number of Urbit identities that function as reputation primitives.
92+
93+### How secure is Urbit right now? {#urbit-project-2}
94+
95+Azimuth, the Urbit identity layer, is ready for serious use. Its contracts have been audited by trustworthy third parties. We encourage everyone to use Azimuth as an identity solution, and to build software projects on top of it.
96+
97+The rest of the Urbit project is still in research-mode. Arvo is a fun operating system to play with, but it’s not yet a safe place to store sensitive data.
98+
99+### How can I contribute to Urbit? {#urbit-project-3}
100+
101+We encourage outside contributors to become a part of the project. The best way to do this is to check out [the Urbit GitHub](https://github.com/urbit) and look at the pinned repositories. Feel free to open issues and make pull requests.
102+
103+You’re also invited to join the [urbit-dev](https://groups.google.com/a/urbit.org/forum/#!forum/dev) Google group and public mailing list.
104+
105+### What is Tlon? {#urbit-project-4}
106+
107+Tlon is the San Francisco-based company that develops Urbit. Tlon doesn’t own Urbit, since Urbit is an open-source project with decentralized ownership.
108+
109+### Who works at Tlon? {#urbit-project-5}
110+
111+You can see who we are at [tlon.io](https://tlon.io/).
112+
113+## Azimuth
114+
115+### What is Azimuth? {#azimuth-1}
116+
117+Azimuth is a general-purpose identity system. It is the Urbit identity base-layer. This system is implemented as a suite of smart contracts on the Ethereum blockchain, and it determines which Ethereum addresses own which Urbit identities, called points. There is a fixed number of possible points, and they exist in three classes. Urbit uses it in Arvo, the Urbit OS.
118+
119+### What are stars, galaxies, and planets? {#azimuth-2}
120+
121+Points come in three classes: galaxies, stars, and planets. The length of a point’s name will tell you its class. Galaxies are 8-bit and have names like `~mul`. Galaxies issue 16-bit stars (`~dacmul`), which can themselves issue 32-bit planets (`~laptel-holfur`).
122+
123+Planets are intended for everyday use by an adult human, and there are 4.3 billion of them (2 to the 32nd power). Stars and galaxies, on the other hand, are meant to act as network infrastructure: on the Arvo network they provide routing and are responsible for distributing software updates.
124+
125+### How many planets, stars, and galaxies are active? {#azimuth-3}
126+
127+The raw data on most Azimuth events that have occurred can be found [here](https://azimuth.network/stats/events.txt). We’re currently working on generalized tooling for viewing these events.
128+
129+### What are comets and moons? {#azimuth-4}
130+
131+In addition to the three classes of Azimuth points mentioned above, there are two other kinds of Urbit identities that are not registered on Azimuth.
132+
133+Moons are 64 bits, are issued by planets, and have names like `~doznec-salfun-naptul-habrys`. Moons are meant for connected devices: phones, desktops, smart TVs, digital thermostats. Moons are subordinated to their parent planet.
134+
135+Comets are 128 bits and have no parents. They can be launched by anyone. They are temporary, disposable identities. Being disposable and essentially unlimited, they will likely not be trusted by default by others on the Arvo network. They have long, hard-to-memorize names, like `~racmus-mollen-fallyt-linpex--watres-sibbur-modlux-rinmex`.
136+
137+### What is a `@p`? Why is my username generated for me? {#azimuth-5}
138+
139+A `@p` (pronounced *pat-pee*) is a name like `~zod` or `~lodleb-ritrul` composed of pronounceable, three-letter phonemic elements like `zod`, `lod`, `leb`, `rit` and `rul`. Shorter names, such as `~zod` and `~marzod` are assigned to ships with special duties on the Arvo network ([stars and galaxies](@/docs/concepts/galaxies-stars-and-planets.md), respectively. Longer names like `~palfun-foslup` are identities for typical users.
140+
141+These names are pre-generated because they map directly to a number in the urbit address space. Galaxies occupy the 8-bit address space, so any galaxy is actually a number between zero to 255. Stars occupy the 16-bit address space, and planets occupy the 32-bit address space.
142+
143+### Can I change my `@p`? {#azimuth-6}
144+
145+Unfortunately not, there is a 1:1 mapping between name and point.
146+
147+### How do I get an identity? {#azimuth-7}
148+
149+Find someone to give you one. This will probably involve purchasing one and having it transferred to an Ethereum address that you own. We recommend using our [Bridge](https://github.com/urbit/bridge/releases) software to access the address that the point is transferred to.
150+
151+### How do I send an identity to someone else? {#azimuth-8}
152+
153+Access the Ethereum address that holds the point you wish to transfer via [Bridge](https://github.com/urbit/bridge/releases).
154+
155+### What is the best way to access Azimuth? {#azimuth-9}
156+
157+We recommended using [Bridge](https://github.com/urbit/bridge/releases) for all Azimuth-related operations. It’s great for managing your points, as well as for viewing information about points you don’t own. We have seen publicly hosted versions of Bridge online and we do not recommend using these websites for security and privacy reasons.
158+
159+## Urbit HD Wallet
160+
161+### What is the Urbit HD Wallet? {#wallet-1}
162+
163+The Urbit Hierarchical Deterministic (HD) Wallet is a system that uses public-key cryptography to secure the ownership of Urbit assets. This means that the wallet system uses public/private key-pairs to make sure that only you have access to it. Such a key-pair has two components: a public key that may be disseminated widely, and a private key that only the owner should know. The former should be treated like a login, and the latter should be treated like a password.
164+
165+The Urbit HD Wallet is different from cryptocurrency wallets that you may be familiar with. The Urbit HD Wallet is not a single Ethereum key-pair that holds full power over its assets, but a system of many related Ethereum key-pairs that have distinct powers. To learn about the specifics, check out our [HD Wallet documentation](@/docs/concepts/azimuth.md#the-urbit-hd-wallet).
166+
167+### What is a master ticket? {#wallet-2}
168+
169+In the context of the Urbit HD Wallet, master ticket is the entropy seed from which all of your other keys are derived. It should be treated like a master password: **you should never share it with anyone, and you must store it very securely (see our practices below).** This ticket can derive your ownership address and all of your proxy keys. You’ll have a ticket if you used the Urbit Wallet Generator or claimed a ship on our hosted version of Bridge.
170+
171+### What is an ownership address? {#wallet-3}
172+
173+An ownership address is an Ethereum address that has full rights over points assigned to it. It can transfer points to other people, meaning that it’s very important to secure its associated private key.
174+
175+### What are proxies? {#wallet-4}
176+
177+Proxies are Ethereum addresses that can be thought of as sibling sub-wallets of your ownership address. They are meant to be used much more often than the ownership address, and have more restrictive, less valuable rights than the ownership address. These rights include spawning child points, voting, and setting networking keys.
178+- Management Proxy: Can configure or set Arvo networking keys and conduct sponsorship related operations.
179+- Voting Proxy: Galaxies only. Galaxies are the part of the galactic senate, and this means they can cast votes on new proposals including changes to the Ecliptic.
180+- Spawn Proxy: For stars and galaxies only. Can create new child points.
181+
182+### What are seeds? {#wallet-5}
183+
184+All Ethereum key-pairs in the Urbit wallet system, including proxies, are produced by 128-bit values called seeds. These seeds are yours alone. An ownership key key-pair is derived from an ownership seed. Also derived from the ownership seed are various proxy seeds, which proxy key-pairs are themselves derived from.
185+
186+### What does it mean to “set public keys”? {#wallet-6}
187+
188+This means registering the public keys of your point's encryption and authentication keys (together known as "networking keys") with Azimuth, so that others can discover them. The corresponding private keys can then be used to, for example, run a ship on the Arvo network.
189+
190+You want to reset these keys if they are compromised, and periodically for maximum security.
191+
192+### What do I do if I want to own multiple points? {#wallet-7}
193+
194+We recommend using different HD Wallets for each point. You are able to assign any number of points to a single Ethereum address, however.
195+
196+### How should I take care of my Urbit HD Wallet? {#wallet-8}
197+
198+Urbit points have accompanying security realities that must be taken seriously. Cryptographic assets are unique among things of value, because all of the responsibility for keeping those assets safe rests with the party that owns them.
199+
200+But the nature of decentralization means that there is no authority that has the power to restore any lost or stolen wallet. Neither can anyone force you to follow good security practices. At most, they can give you recommendations. **Remember:** if critical items, such as your ownership key, are lost or compromised, your assets are likely gone forever.
201+
202+Below we list some good practices for storing keys, strictest first. Higher-value assets should be secured with stricter measures.
203+
204+**Security Tier 1: Cold Storage**
205+
206+Cold storage refers to any method in which secrets are stored in a way that is not accessible to any network. Cold-stored keys should only ever be generated offline.
207+
208+Cold storage media options:
209+- Printing the secret on a piece of paper. However, paper wallets are vulnerable to various forms of physical damage, such as rot, water damage, smoke, or fire. Laminating the paper can mitigate some of these risks, but the lamination can potentially trap moisture. Make sure that you trust the printer: some have memory and network connections.
210+- Storing the secret on a brand-new USB stick or hardware wallet that is never connected to a networked machine.
211+- Engraving the secret on a strip of stainless steel. This medium is resistant to both water damage and fire damage.
212+
213+Places to store your cold-storage media:
214+- A hidden safe in your home.
215+- A safe-deposit box at a bank.
216+
217+It’s a good idea to store your keys redundantly; for example, on both a USB stick and a piece of paper in the safe, in case one of those methods fails. If you deem a key to be valuable enough, you can **shard** it into thirds (or other splits) and store each third in secure, geographically distributed locations.
218+
219+**Security Tier 2: Hardware Wallet**
220+
221+A hardware wallet is a digital storage device that’s purpose-built to store cryptographic secrets. They are unaffected by typical key-stealing malware, and have built-in security mechanisms that other digital devices lack. Do research and make sure that you are buying an authentic device manufactured by trustworthy, technically competent security experts with a good reputation. Trezor and Ledger are two popular brands of hardware wallets.
222+
223+At this tier, it’s permissible to connect a hardware wallet to your networked machine.
224+
225+**Security Tier 3: On Your Computer**
226+
227+This tier includes any method where secrets are stored on an everyday computing platform. Some such methods are:
228+
229+- Encrypted PDFs containing a secret on your desktop’s drive.
230+- Storing secrets on a cloud account protected by multi-factor authentication.
231+
232+This method is risky for a number of reasons. Networked computers can contain malware. Computers that see common use are also prone to crashes and data loss. Storing secrets on cloud accounts mitigates the risk of data destruction, but it exposes a much larger attack surface to malicious actors.
233+
234+For all of these reasons, make sure that you only use Tier 3 methods for the storage of low-value secrets.
235+
236+### I have a galaxy or star with lockup conditions. How does this work? {#wallet-9}
237+
238+There are two kinds of release schemes for locked up assets: linear and conditional.
239+
240+In either scheme, you start out being able to take one star out of lockup, regardless of the terms set around the lockup as a whole. This way, you get to participate with a star right away. Become useful!Go do something cool!
241+
242+If your lockup involved a galaxy, all its stars will be locked up, but you will have immediate, lock-free control of the galaxy. You will likely need it to use that star.
243+
244+Note that the "releasing" of stars just means that they become available for you to claim. They don't automatically get transferred to you, you have to withdraw them from the appropriate lockup contract.
245+
246+Linear release is the simplest, and does exactly what it sounds like. Your stars will be released linearly over a period of time. Most commonly, this is a period of four years. If you have four stars in lockup, that means you will be able to withdraw one star per year. In many cases, there is also an initial windup period, which has to pass before the linear releasing begins. Usually, this is one year. Since Azimuth launched in January 2019, this means the linear release will begin in January 2020.
247+
248+Conditional release is a bit more complicated. If your stars are in conditional lockup, they're likely divided over three so-called tranches. Each of these unlocks only after a unique condition is met. Since it's difficult to verify things about the real world using smart contracts, we instead have the Galactic Senate verify that they've been met. Once the Senate marks a condition for a tranche as cleared, it starts releasing linearly over the period of a year.
249+
250+## Arvo
251+
252+### What is Arvo? {#arvo-1}
253+
254+Arvo is the Urbit operating system. Unlike conventional operating systems, it’s functional and deterministic. Arvo is written in Hoon, our purely functional programming language.
255+
256+Instances of the Arvo operating system, called ships, communicate as peers on what we refer to as “the Arvo network.” The Arvo network succeeds where other peer-to-peer networks have failed. Using Azimuth, Arvo ships can prove their identities to one another.
257+
258+### What is unique about Arvo? {#arvo-2}
259+
260+Arvo is much different from other operating systems because of its determinism. Processing in the system happens in a unique way: when an event happens, a transition function takes that event and the old state of Arvo, and then produces an effect and a new state of Arvo. To visualize:
261+
262+[event, old state] -> [effects, new state]
263+
264+Thus, all events are permanently logged in the system, and you can always go back and see the state of the system at any point in time.
265+
266+### How is Arvo connected to Ethereum? {#arvo-3}
267+
268+When an Arvo ship (instance) is started for the first time, you must use a key-file containing the private keys for your Azimuth point's networking keys. Azimuth uses the Ethereum blockchain as its decentralized ledger.
269+
270+See our [Getting Started](@/docs/getting-started/_index.md) guide to learn how to get your ship onto the Arvo network.
271+
272+### Will the Arvo network survive if Ethereum dies? {#arvo-4}
273+
274+Yes. It would be annoying, but Azimuth would be ported to another kind of decentralized ledger.
275+
276+### How do I install Arvo? {#arvo-5}
277+
278+Check out our guides [here](@/docs/getting-started/_index.md)
279+
280+### How do I use Arvo? {#arvo-6}
281+
282+The [Using](@/docs/using/_index.md) section of the documentation will help you out.
283+
284+### How do over-the-air updates work? {#arvo-7}
285+
286+Your sponsor, a star or galaxy that your ship “lives” under, may send you new source code for your system. When you receive new source code from your sponsor, your system recompiles itself using that code, performs any necessary data migrations, and keeps running. Ideally this happens seamlessly without the user even noticing, although there is sometimes a slowdown while rebuilding the system from source.
287+
288+### What is Landscape? {#arvo-8}
289+
290+Landscape is an experimental Arvo web interface that includes social functions such as chat and publishing. You can access Landscape by navigating to http://localhost:8080 or https://<your-planet>.arvo.network, where <your-planet> is your running ship, connected to the Arvo network.
291+
292+### I have a point, now what? {#arvo-9}
293+
294+Follow our guide on how to boot a ship [here](@/docs/getting-started/booting-a-ship.md).
295+
296+Get on the [mailing list](/../#mc_embed_signup). Learn [Hoon](@/docs/learn/hoon/_index.md).
297+
298+## Grants
299+
300+### What are the different types of grants? {#grants-1}
301+
302+There are three ways to receive a grant of stars for your contribution to the Urbit ecosystem.
303+- You can submit a **Proposal** if you’re interested in working on a project of your own creation for a number of stars that you pitch.
304+- You can apply to claim a **Bounty.** Bounties are requests for work created by Tlon that are claimable by the public, with a predetermined number of stars as the reward.
305+- The **Gifts** program, which is Tlon’s informal way of rewarding contributions that don’t fall into the above categories. There is no way to apply for a gift.
306+
307+### How do I get a gift? {#grants-2}
308+
309+Make useful and substantial contributions to Urbit. These can be contributions to the project itself, or they can be outside tools that aren’t part of the Urbit codebase. There’s no formal system for determining who gets a gift; it’s at the discretion of employees at Tlon. Nothing is guaranteed, but you can get an idea for what kinds of contributions results in gifts by looking at the [gifts history.](https://grants.urbit.org/history)
310+
311+### How often are gifts awarded? {#grants-3}
312+
313+Gifts are awarded semiannually.
314+
315+### What happens if a user does not complete a proposal but meets some milestones? {#grants-4}
316+
317+You must complete the proposal to be guaranteed to receive the star grant. Partial star payouts can be negotiated for incomplete work, but such payouts are at the discretion of Tlon.
318+
319+However, either party may terminate a proposal on seven days’ written notice. In such a scenario, you may be paid pro rata for any portion of the proposal that has been completed.
320+
321+### Do I have to pay taxes on stars? {#grant-5}
322+
323+Yes. If you are American, we need your W-9 before we can pay you your stars. If you’re not an American, you don’t need to get us any forms, but you do need to personally follow the relevant tax laws in your jurisdiction.
324+
325+### Can I create a bounty? {#grants-6}
326+
327+No. Bounties are only created by Tlon, but we draw inspiration for bounties from various sources, including the community. If you want to pitch a project, use the Proposals system.
328+
329+### How are proposals approved? {#grants-7}
330+
331+The manager of the Grants program approves them after consulting with the relevant members of the engineering team. This proposal can be accepted, rejected, or accepted with changes by Tlon. The proposal’s star payout can also be adjusted.
332+
333+### If I win a proposal, do I have an exclusive claim on the project? {#grants-8}
334+
335+You have a claim to the reward of the project as long as milestones are met to the satisfaction of Tlon. However, Tlon may allow another developer to work on the same project in parallel, for a separate reward.
336+
337+### Will milestones be embedded in a smart contract? {#grants-9}
338+
339+No.
340+
341+### Does reputation play a role in the assessment of grants? {#grants-10}
342+
343+Yes. We consider this to be a feature.
344+
345+### I completed a proposal. When will I receive payment? {#grants-11}
346+
347+We will pay you within a 30-day window.
348+
349+### Can I see the proposals of others that have been accepted? {#grants-12}
350+
351+Yes. Check out our [proposals page.](https://grants.urbit.org/proposals)
352+
353+### How do I safely store my stars? {#grants-13}
354+
355+We recommend storing each star in its own in its own Ethereum wallet. The private keys to these wallets should be stored on physical media that is not connected to a networked computer. Redundancies don’t hurt!
356+
357+### Can I delegate a bounty or proposal to someone else? {#grants-14}
358+
359+No. Not unless explicitly authorized by Tlon.
360+[Make stuff](@/docs/getting-started/contributing.md).
content/hoonschool.mdnew file+23
@@ -0,0 +1,23 @@
1++++
2+title = "Hoon School"
3++++
4+
5+Interested in developing on Urbit? You’ll want to learn [Hoon](https://urbit.org/docs/learn/hoon/_index.md), a functional programming language that’s custom-designed for use with Arvo, the Urbit operating system.
6+
7+We want you to learn Hoon, too. That’s why we’re offering Hoon School: a free, asynchronous program that’s fully supported by an instructor. Hoon School is offered in the form of two courses: Hoon 101 and Hoon 201. Classes are offered three times a year.
8+
9+[Apply below ↓](#apply)
10+
11+## Hoon 101
12+
13+Hoon 101 is a six-week, introductory-level course. It’s appropriate for motivated learners with no programming experience, and for those familiar with other programming languages. You’ll leave Hoon 101 with a strong understanding of the language’s fundamentals. Expect to devote four hours a week to this course.
14+
15+## Hoon 201
16+
17+Hoon 201 is an eight-week, advanced-level course with a heavier workload. It’s appropriate for graduates of Hoon 101, and for those otherwise already familiar with Hoon's foundations. After graduating from Hoon 201, you’ll have enough knowledge to build applications and contribute code to the Arvo operating system. Expect to devote six hours a week to this class.
18+
19+## Which should I take?
20+
21+If Hoon would be your first language, or you have some experience with non-functional languages, apply to Hoon 101. If you’ve already played around with Hoon extensively and you’re a seasoned professional programmer, apply to Hoon 201. If you’re on the fence, go with 101; the two courses scheduled back-to-back, so you can enroll in the advanced course upon graduating from the introductory one.
22+
23+<script class="src="https://static.airtable.com/js/embed/embed_snippet_v1.js"></script><iframe id="apply" class="airtable-embed airtable-dynamic-height w-full" src="https://airtable.com/embed/shr8bb15QPR3hfQpG?backgroundColor=teal" frameborder="0" height="1300"></iframe>
content/posts/2016-5-16-roadmap.mdmode 100755 → 100644

Only the mode changed.

content/posts/2016-7-20-update.mdmode 100755 → 100644

Only the mode changed.

content/posts/azimuth-is-on-chain.md+2−8
@@ -7,11 +7,11 @@ aliases = ["/posts/essays/azimuth-is-on-chain"]
7[taxonomies]
7[taxonomies]
8posts = ["Essays"]
8posts = ["Essays"]
9+++
9+++
10+
10![](https://storage.googleapis.com/media.urbit.org/site/arvo-4.svg)
11![](https://storage.googleapis.com/media.urbit.org/site/arvo-4.svg)
11
12
12The Urbit address space is now live on the Ethereum blockchain. We’re calling the PKI ‘Azimuth’ and you can find it at [`0x223c067f8cf28ae173ee5cafea60ca44c335fecb`](https://etherscan.io/address/0x223c067f8cf28ae173ee5cafea60ca44c335fecb) or [`azimuth.eth`](https://etherscan.io/address/azimuth.eth). Owners of Azimuth ‘points’ (galaxies, stars or planets) can use [Bridge](https://github.com/urbit/bridge/releases) to manage them and view their balance now.
13The Urbit address space is now live on the Ethereum blockchain. We’re calling the PKI ‘Azimuth’ and you can find it at [`0x223c067f8cf28ae173ee5cafea60ca44c335fecb`](https://etherscan.io/address/0x223c067f8cf28ae173ee5cafea60ca44c335fecb) or [`azimuth.eth`](https://etherscan.io/address/azimuth.eth). Owners of Azimuth ‘points’ (galaxies, stars or planets) can use [Bridge](https://github.com/urbit/bridge/releases) to manage them and view their balance now.
13
14
14−
15Sometime in the next few days, owners of Azimuth points will be able to boot Arvo, the Urbit OS, from their Azimuth point and request access to one of our 'cities': private communities for chat and discussion. These new cities use Landscape, a brand new UI for using Urbit in the browser.
15Sometime in the next few days, owners of Azimuth points will be able to boot Arvo, the Urbit OS, from their Azimuth point and request access to one of our 'cities': private communities for chat and discussion. These new cities use Landscape, a brand new UI for using Urbit in the browser.
16
16
17This means you can boot a deterministic OS from an identity you own, join a totally independent, decentralized and self-governing network and run a clean, simple UI. Pretty exciting. Is it ready for ordinary users? Definitely not.
17This means you can boot a deterministic OS from an identity you own, join a totally independent, decentralized and self-governing network and run a clean, simple UI. Pretty exciting. Is it ready for ordinary users? Definitely not.
@@ -26,7 +26,6 @@ We aren't calling this a launch. There's no keynote. There's no dancing on stage
26
26
27> Note: As of `~2019.1.14` we're in the process of rebooting the Arvo network. We expect it to be back online before the end of the week. We'll update this post and [tweet](https://twitter.com/urbit) when the network is online and stable.
27> Note: As of `~2019.1.14` we're in the process of rebooting the Arvo network. We expect it to be back online before the end of the week. We'll update this post and [tweet](https://twitter.com/urbit) when the network is online and stable.
28
28
29−
30## Rethinking the stack
29## Rethinking the stack
31
30
32We've started to talk about Urbit as a stack of three parts: Azimuth, the address space and identity layer on Ethereum; Arvo, the OS and overlay network; and Aegean, a pattern for building decentralized communities on the Arvo network.
31We've started to talk about Urbit as a stack of three parts: Azimuth, the address space and identity layer on Ethereum; Arvo, the OS and overlay network; and Aegean, a pattern for building decentralized communities on the Arvo network.
@@ -35,7 +34,6 @@ We tried to capture this separation in [the primer](/primer), a new overview of
35
34
36Let's step through them to see what's new.
35Let's step through them to see what's new.
37
36
38−
39## Azimuth
37## Azimuth
40
38
41![](https://storage.googleapis.com/media.urbit.org/site/bridge-0.png)
39![](https://storage.googleapis.com/media.urbit.org/site/bridge-0.png)
@@ -52,7 +50,7 @@ To manage your points, we built [Bridge](https://github.com/urbit/bridge/release
52
50
53If you're curious, you can find the source for the contracts in the [`urbit/azimuth`](https://github.com/urbit/azimuth) repo. The Bridge source is in the [`urbit/bridge`](https://github.com/urbit/bridge) repo.
51If you're curious, you can find the source for the contracts in the [`urbit/azimuth`](https://github.com/urbit/azimuth) repo. The Bridge source is in the [`urbit/bridge`](https://github.com/urbit/bridge) repo.
54
52
55−We had the source for our contracts audited by Open Zeppelin, Blockchain at Berkeley and Blocktrax. Did we miss something? We sure hope not — but we'd happily compensate anyone who can find bugs. Please reach out to security@urbit.org if you find something of concern. We'd be happy to hear from you.
53+We had the source for our contracts audited by Open Zeppelin, Blockchain at Berkeley and Bloctrax. Did we miss something? We sure hope not — but we'd happily compensate anyone who can find bugs. Please reach out to security@urbit.org if you find something of concern. We'd be happy to hear from you.
56
54
57![](https://storage.googleapis.com/media.urbit.org/site/sigils.svg)
55![](https://storage.googleapis.com/media.urbit.org/site/sigils.svg)
58
56
@@ -60,7 +58,6 @@ Azimuth names have always had a pronunciation system. `~ronryd-nidseg` is 13,695
60
58
61So, how do you get an Azimuth point? We've left that question unanswered intentionally. Azimuth points are meant to be like digital land, not currency. They should change hands slowly. In order to get an Azimuth point, you'll need to find someone who already has one and is willing to part with it.
59So, how do you get an Azimuth point? We've left that question unanswered intentionally. Azimuth points are meant to be like digital land, not currency. They should change hands slowly. In order to get an Azimuth point, you'll need to find someone who already has one and is willing to part with it.
62
60
63−
64## Arvo
61## Arvo
65
62
66![](https://storage.googleapis.com/media.urbit.org/site/fast-lux-3.gif)
63![](https://storage.googleapis.com/media.urbit.org/site/fast-lux-3.gif)
@@ -71,7 +68,6 @@ It's tough to talk about the progress we've made on Arvo without writing a long
71
68
72There's plenty of work to do, though. Should you be building on top of Arvo? Probably not yet. The kernel and kernel modules are really where the work still lies. We build things on top of Arvo, and in doing so it's pretty clear that the system can be better. Clay (the filesystem) and Gall (the application model) are due for a rewrite. A complete rewrite of the web server, Eyre is almost finished.
69There's plenty of work to do, though. Should you be building on top of Arvo? Probably not yet. The kernel and kernel modules are really where the work still lies. We build things on top of Arvo, and in doing so it's pretty clear that the system can be better. Clay (the filesystem) and Gall (the application model) are due for a rewrite. A complete rewrite of the web server, Eyre is almost finished.
73
70
74−
75## Aegean
71## Aegean
76
72
77![](https://storage.googleapis.com/media.urbit.org/site/landscape-inbox.png)
73![](https://storage.googleapis.com/media.urbit.org/site/landscape-inbox.png)
@@ -90,7 +86,6 @@ Our first agent is called [Landscape](https://github.com/urbit/landscape), an ag
90
86
91Anyone can create their own city, of course. The source is open. But we're not going to encourage you to just yet. Feel free to explore, but also be patient. We've got a lot planned around this. If Azimuth is done and Arvo is young, Aegean is just being born.
87Anyone can create their own city, of course. The source is open. But we're not going to encourage you to just yet. Feel free to explore, but also be patient. We've got a lot planned around this. If Azimuth is done and Arvo is young, Aegean is just being born.
92
88
93−
94## Tlon
89## Tlon
95
90
96![](https://storage.googleapis.com/media.urbit.org/site/office-1.jpg)
91![](https://storage.googleapis.com/media.urbit.org/site/office-1.jpg)
@@ -101,7 +96,6 @@ One important omission on `tlon.io` is Curtis. As of today, he's no longer worki
101
96
102One major reason for that confidence is that Erik and Anthony have now become COO and CPO respectively. Having thoughtful, organized people leading the project is a huge benefit to the whole team. I'm very glad to have them on board.
97One major reason for that confidence is that Erik and Anthony have now become COO and CPO respectively. Having thoughtful, organized people leading the project is a huge benefit to the whole team. I'm very glad to have them on board.
103
98
104−
105## Looking ahead
99## Looking ahead
106
100
107I certainly forgot at least a thing or two. We'll dive in to more detail in the coming weeks.
101I certainly forgot at least a thing or two. We'll dive in to more detail in the coming weeks.
content/posts/blockchannel-episode-19-virtual-land-in-the-clouds-with-urbit.mdmode 100755 → 100644

Only the mode changed.

content/posts/common-objections-to-urbit.md+3−8
@@ -10,9 +10,8 @@ posts = ["Essays"]
10![](https://media.urbit.org/site/blog-9.jpg)
10![](https://media.urbit.org/site/blog-9.jpg)
11
11
12We love crowdsourced criticism. We've heard a lot of great
12We love crowdsourced criticism. We've heard a lot of great
13−objections to Urbit. Surely there's more out there! If so,
13+objections to Urbit. Surely there's more out there! We'll
14−comments are open at the bottom of the page. We'll revise this
15−document to include anything new and interesting.
14+revise this document to include anything new and interesting.
16
15
17Here's our list right now:
16Here's our list right now:
18
17
@@ -356,11 +355,7 @@ expressive power; but identity is optimal for the UI of the
356common case. We'd need to see the Powerbox, Sandstorm's UI, more
355common case. We'd need to see the Powerbox, Sandstorm's UI, more
357fully fleshed out to understand its UI approach better.
356fully fleshed out to understand its UI approach better.
358
357
359−;=
360− ;h2
361− ;div(id "planet"): Urbit doesn't have enough planets for every human
358+## <a name="planet"></a>Urbit doesn't have enough planets for every human
362− ==
363−==
364
359
365A 32-bit planet is a tool, not a toy. Like a car, it's a device
360A 32-bit planet is a tool, not a toy. Like a car, it's a device
366for a responsible and independent adult. There aren't 4 billion
361for a responsible and independent adult. There aren't 4 billion
netlify.toml+3−3
@@ -1,9 +1,9 @@
1[build]
1[build]
2 publish = "public"
2 publish = "public"
3− command = "curl -sL https://github.com/getzola/zola/releases/download/v0.5.1/zola-v0.5.1-x86_64-unknown-linux-gnu.tar.gz | tar zxv && ./zola build"
3+ command = "curl -sL https://github.com/getzola/zola/releases/download/v0.8.0/zola-v0.8.0-x86_64-unknown-linux-gnu.tar.gz | tar zxv && ./zola build"
4
4
5[build.environment]
5[build.environment]
6− ZOLA_VERSION = "0.5.1"
6+ ZOLA_VERSION = "0.8.0"
7
7
8[context.deploy-preview]
8[context.deploy-preview]
9− command = "curl -sL https://github.com/getzola/zola/releases/download/v0.5.1/zola-v0.5.1-x86_64-unknown-linux-gnu.tar.gz | tar zxv && ./zola build --base-url $DEPLOY_PRIME_URL"
9+ command = "curl -sL https://github.com/getzola/zola/releases/download/v0.8.0/zola-v0.8.0-x86_64-unknown-linux-gnu.tar.gz | tar zxv && ./zola build --base-url $DEPLOY_PRIME_URL"
sass/_main.scss+64−5
@@ -1,7 +1,7 @@
1/*
1/*
2 * core
2 * core
3 */
3 */
4−
4+@import "indigo.scss";
5body {
5body {
6 font-family: 'Inter UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
6 font-family: 'Inter UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
7 font-size: (3.5rem / 4);
7 font-size: (3.5rem / 4);
@@ -54,6 +54,15 @@ iframe {
54 margin-top: -(24rem / 4);
54 margin-top: -(24rem / 4);
55}
55}
56
56
57+// Hack to left-align top-bar text with content
58+.top-bar-inner {
59+ max-width: 960px;
60+ margin: 0 auto;
61+ p {
62+ margin-left: (100% / 6);
63+ }
64+}
65+
57.content-nav {
66.content-nav {
58 width: 20%;
67 width: 20%;
59 min-width: 200px;
68 min-width: 200px;
@@ -336,19 +345,37 @@ iframe {
336 }
345 }
337}
346}
338
347
348+#search-reset {
349+ cursor: pointer;
350+ user-select: none;
351+}
352+
353+.form--big {
354+ max-width: 390px;
355+ .btn {
356+ width: 100%;
357+ }
358+}
359+
360+.hoonschool-application {
361+ &__experience {
362+ resize: vertical;
363+ }
364+}
365+
339/*
366/*
340 * styling
367 * styling
341 */
368 */
342
369
343− // remove default x icon
370+// remove default x icon
344 input[type="search"]::-webkit-search-decoration,
371 input[type="search"]::-webkit-search-decoration,
345 input[type="search"]::-webkit-search-cancel-button,
372 input[type="search"]::-webkit-search-cancel-button,
346 input[type="search"]::-webkit-search-results-button,
373 input[type="search"]::-webkit-search-results-button,
347 input[type="search"]::-webkit-search-results-decoration {
374 input[type="search"]::-webkit-search-results-decoration {
348 display: none;
375 display: none;
349− }
376+}
350
377
351−textarea, input[type="text"], input[type="email"], input[type="number"], input[type="search"] {
378+input[type="search"] {
352 border: 1px solid #B1B2B3;
379 border: 1px solid #B1B2B3;
353 font-size: (4rem / 4);
380 font-size: (4rem / 4);
354 line-height: (6rem / 4);
381 line-height: (6rem / 4);
@@ -356,6 +383,10 @@ textarea, input[type="text"], input[type="email"], input[type="number"], input[t
356 padding-right: 40px;
383 padding-right: 40px;
357 position: relative;
384 position: relative;
358 border-radius: 0 !important;
385 border-radius: 0 !important;
386+}
387+
388+textarea, input[type="text"], input[type="email"], input[type="number"], input[type="search"], select {
389+ border: 1px solid #000;
359 padding: 12px;
390 padding: 12px;
360 width: 100%;
391 width: 100%;
361 outline: none;
392 outline: none;
@@ -370,6 +401,10 @@ textarea, input[type="text"], input[type="email"], input[type="number"], input[t
370 }
401 }
371}
402}
372
403
404+textarea {
405+ resize: horizontal;
406+}
407+
373button, .btn {
408button, .btn {
374 font-family: 'Inter UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
409 font-family: 'Inter UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
375 font-weight: 500;
410 font-weight: 500;
@@ -537,7 +572,7 @@ pre, code {
537 font-size: (4rem / 4);
572 font-size: (4rem / 4);
538}
573}
539
574
540−textarea, input[type="text"], input[type="email"], input[type="number"] {
575+textarea, input[type="text"], input[type="email"], input[type="number"], select {
541 border: 1px solid #B1B2B3;
576 border: 1px solid #B1B2B3;
542 font-family: "Source Code Pro", mono;
577 font-family: "Source Code Pro", mono;
543 font-size: (4rem / 4);
578 font-size: (4rem / 4);
@@ -577,3 +612,27 @@ textarea, input[type="text"], input[type="email"], input[type="number"] {
577 padding: 0;
612 padding: 0;
578 text-align: inherit;
613 text-align: inherit;
579}
614}
615+
616+// Markdown
617+summary {
618+ font-size: (5rem / 4);
619+ font-weight: 500;
620+ padding-bottom: (2rem / 4);
621+ padding-top: (2rem / 4);
622+ cursor: pointer;
623+ user-select: none;
624+ -webkit-user-select: none;
625+}
626+
627+summary:focus {
628+ outline: none;
629+}
630+
631+details {
632+ padding-bottom: (1rem / 4);
633+ padding-top: (1rem / 4);
634+ ul, ol {
635+ padding-left: 0;
636+ list-style: none;
637+ }
638+}
sass/indigo.scss+3
@@ -52,6 +52,9 @@ samp {
52 font-family: monospace, monospace;
52 font-family: monospace, monospace;
53 font-size: 1em;
53 font-size: 1em;
54}
54}
55+code {
56+ white-space: pre;
57+}
55small {
58small {
56 font-size: 80%;
59 font-size: 80%;
57}
60}
static/assets/back-arrow.svgnew file+3
@@ -0,0 +1,3 @@
1+<svg width="13" height="12" viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<path d="M5.68537 11.1477L6.85582 9.97727L3.62287 6.75568H12.1399V5.0625H3.62287L6.85582 1.82955L5.68537 0.670454L0.446733 5.90909L5.68537 11.1477Z" fill="black"/>
3+</svg>
static/php/airtable/hoonschool.phpnew file+3
@@ -0,0 +1,3 @@
1+<?php
2+ echo "test";
3+?>
static/php/airtable/src/Airtable.phpnew file+118
@@ -0,0 +1,118 @@
1+<?php
2+
3+namespace TANIOS\Airtable;
4+
5+/**
6+ * Airtable API Class
7+ *
8+ * @author Sleiman Tanios
9+ * @copyright Sleiman Tanios - TANIOS 2017
10+ * @version 1.0
11+ */
12+
13+
14+class Airtable
15+{
16+
17+ const API_URL = "https://api.airtable.com/v0/";
18+
19+ private $_key;
20+
21+ private $_base;
22+
23+ public function __construct($config)
24+ {
25+ if (is_array($config)) {
26+ $this->setKey($config['api_key']);
27+ $this->setBase($config['base']);
28+ } else {
29+ echo 'Error: __construct() - Configuration data is missing.';
30+ }
31+ }
32+
33+ public function setKey($key)
34+ {
35+ $this->_key = $key;
36+ }
37+
38+ public function getKey()
39+ {
40+ return $this->_key;
41+ }
42+
43+ public function setBase($base)
44+ {
45+ $this->_base = $base;
46+ }
47+
48+ public function getBase()
49+ {
50+ return $this->_base;
51+ }
52+
53+ public function getApiUrl($request){
54+ $request = str_replace( ' ', '%20', $request );
55+ $url = self::API_URL.$this->getBase().'/'.$request;
56+ return $url;
57+ }
58+
59+ function getContent($content_type,$params="",$relations=false)
60+ {
61+ return new Request( $this, $content_type, $params, false, $relations );
62+ }
63+
64+ function saveContent($content_type,$fields)
65+ {
66+
67+ $fields = array('fields'=>$fields);
68+
69+ $request = new Request( $this, $content_type, $fields, true );
70+
71+ return $request->getResponse();
72+
73+ }
74+
75+ function updateContent($content_type,$fields)
76+ {
77+
78+ $fields = array('fields'=>$fields);
79+
80+ $request = new Request( $this, $content_type, $fields, 'patch' );
81+
82+ return $request->getResponse();
83+
84+ }
85+
86+ function deleteContent($content_type)
87+ {
88+
89+ $request = new Request( $this, $content_type, [], 'delete' );
90+
91+ return $request->getResponse();
92+
93+ }
94+
95+ function quickCheck($content_type,$field="",$value="")
96+ {
97+ $params = "";
98+
99+ if (!empty($field)&& !empty($value)){
100+
101+ $params = array(
102+ "filterByFormula" => "AND({{$field}} = '$value')",
103+ );
104+ }
105+
106+ $request = new Request( $this, $content_type, $params, false );
107+
108+ $response = $request->getResponse();
109+
110+
111+ $results['count'] = count($response->records);
112+ $results['records'] = $response->records;
113+
114+
115+ return (object)$results;
116+ }
117+
118+}
static/php/airtable/src/Request.phpnew file+155
@@ -0,0 +1,155 @@
1+<?php
2+/**
3+ * Created by PhpStorm.
4+ * User: glali
5+ * Date: 2017-04-27
6+ * Time: 14:55
7+ */
8+
9+namespace TANIOS\Airtable;
10+
11+class Request implements \ArrayAccess
12+{
13+
14+ /**
15+ * @var Airtable Instance of Airtable
16+ */
17+ private $airtable;
18+ /**
19+ * @var resource Instance of CURL
20+ */
21+ private $curl;
22+ /**
23+ * @var string Content type
24+ */
25+ private $content_type;
26+ /**
27+ * @var array Request data
28+ */
29+ private $data = [];
30+ /**
31+ * @var bool Is it a POST request?
32+ */
33+ private $is_post = false;
34+
35+ /**
36+ * @var array|boolean Relations to lazy load
37+ */
38+ private $relations;
39+
40+ /**
41+ * Create a Request to AirTable API
42+ * @param Airtable $airtable Instance of Airtable
43+ * @param string $content_type Content type
44+ * @param array $data Request data
45+ * @param bool|string $is_post Is it a POST request?
46+ */
47+ public function __construct( $airtable, $content_type, $data = [], $is_post = false, $relations = false )
48+ {
49+
50+ $this->airtable = $airtable;
51+ $this->content_type = $content_type;
52+ $this->data = $data;
53+ $this->is_post = $is_post;
54+ $this->relations = $relations;
55+
56+ }
57+
58+ private function init()
59+ {
60+
61+ $headers = array(
62+ 'Content-Type: application/json',
63+ sprintf('Authorization: Bearer %s', $this->airtable->getKey())
64+ );
65+
66+ $request = $this->content_type;
67+
68+ if( ! $this->is_post )
69+ {
70+ if (!empty($this->data)){
71+ $data = http_build_query($this->data);
72+ $request .= "?" . $data;
73+ }
74+ }
75+
76+ $curl = curl_init($this->airtable->getApiUrl($request));
77+
78+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
79+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
80+
81+ if( $this->is_post )
82+ {
83+ if( strtolower( $this->is_post ) == 'patch' )
84+ {
85+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PATCH');
86+ }
87+ else if( strtolower( $this->is_post ) == 'delete' )
88+ {
89+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
90+ }
91+ curl_setopt($curl,CURLOPT_POST, count($this->data));
92+ curl_setopt($curl,CURLOPT_POSTFIELDS, json_encode($this->data));
93+ }
94+
95+ $this->curl = $curl;
96+
97+ }
98+
99+ /**
100+ * @return Response Get response from API
101+ */
102+ public function getResponse()
103+ {
104+
105+ $this->init();
106+
107+ $response_string = curl_exec( $this->curl );
108+
109+ $response = new Response( $this->airtable, $this, $response_string, $this->relations );
110+
111+ return $response;
112+
113+ }
114+
115+ public function __set( $key, $value )
116+ {
117+ if( ! is_array( $this->data ) )
118+ {
119+ $this->data = [];
120+ }
121+
122+ $this->data[ $key ] = $value;
123+ }
124+
125+ public function offsetExists($offset)
126+ {
127+ return is_array( $this->data) && isset( $this->data[ $offset ] );
128+ }
129+
130+ public function offsetGet($offset)
131+ {
132+ return is_array( $this->data ) && isset( $this->data[ $offset ] )
133+ ? $this->data[ $offset ]
134+ : null;
135+ }
136+
137+ public function offsetSet($offset, $value)
138+ {
139+ if( ! is_array( $this->data ) )
140+ {
141+ $this->data = [];
142+ }
143+
144+ $this->data[ $offset ] = $value;
145+ }
146+
147+ public function offsetUnset($offset)
148+ {
149+ if( is_array( $this->data ) && isset( $this->data[ $offset ] ) )
150+ {
151+ unset( $this->data[ $offset ] );
152+ }
153+ }
154+
155+}
static/php/airtable/src/Response.phpnew file+246
@@ -0,0 +1,246 @@
1+<?php
2+/**
3+ * Created by PhpStorm.
4+ * User: glali
5+ * Date: 2017-04-27
6+ * Time: 14:44
7+ */
8+
9+namespace TANIOS\Airtable;
10+
11+
12+class Response implements \ArrayAccess
13+{
14+
15+ /**
16+ * @var Airtable Instance of Airtable
17+ */
18+ private $airtable;
19+
20+ /**
21+ * @var Request Instance or Request
22+ */
23+ private $request;
24+
25+ /**
26+ * @var string Response content
27+ */
28+ private $content = "";
29+
30+ /**
31+ * @var bool|\stdClass Response
32+ */
33+ private $parsedContent = false;
34+
35+ /**
36+ * Response constructor.
37+ * @param Airtable $airtable Instance of Airtable
38+ * @param Request $request Instance of Request
39+ * @param string $content Content string
40+ */
41+ public function __construct( $airtable, $request, $content, $relations = false )
42+ {
43+
44+ $this->airtable = $airtable;
45+
46+ $this->request = $request;
47+
48+ $this->content = $content;
49+
50+ try
51+ {
52+ $this->parsedContent = json_decode( $content );
53+ }
54+ catch ( \Exception $e )
55+ {
56+ $this->parsedContent = false;
57+ }
58+
59+ if( is_array( $relations ) && count( $relations ) > 0 )
60+ {
61+
62+ if( array_keys( $relations ) !== range( 0, count( $relations ) - 1 ) )
63+ {
64+ foreach ( $relations as $related_field => $related_table )
65+ {
66+ $this->processRelatedField( $related_field, $related_table );
67+ }
68+ }
69+ else
70+ {
71+ foreach ( $relations as $related_field )
72+ {
73+ $this->processRelatedField( $related_field );
74+ }
75+ }
76+
77+ }
78+
79+ }
80+
81+ private function processRelatedField( $related_field, $related_table = false )
82+ {
83+
84+ if( isset( $this->parsedContent->records ) && is_array( $this->parsedContent->records ) && count( $this->parsedContent->records ) > 0 )
85+ {
86+ foreach ( $this->parsedContent->records as $record_key => $record )
87+ {
88+ $this->parsedContent->records[ $record_key ] = $this->loadRelatedField( $related_field, $related_table, $record );
89+ }
90+ }
91+ else
92+ {
93+ $this->parsedContent = $this->loadRelatedField( $related_field, $related_table, $this->parsedContent );
94+ }
95+
96+ }
97+
98+ private function loadRelatedField( $related_field, $related_table, $record )
99+ {
100+
101+
102+ if( ! isset( $record->fields ) || ! isset( $record->fields->$related_field ) )
103+ {
104+ return $record;
105+ }
106+
107+ if( empty( $related_table ) )
108+ {
109+ $related_table = $related_field;
110+ }
111+
112+ $relation_ids = $record->fields->$related_field;
113+
114+ if( ! is_array( $relation_ids ) )
115+ {
116+ $relation_ids = [ $relation_ids ];
117+ }
118+
119+ $relation_formula = "OR(";
120+ $relation_formula .= implode( ', ', array_map( function( $id ) { return "RECORD_ID() = '$id'"; }, $relation_ids ) );
121+ $relation_formula .= ")";
122+
123+ if( ! is_array( $related_table ) )
124+ {
125+ $relation_request = $this->airtable->getContent( "$related_table", [
126+ 'filterByFormula' => $relation_formula
127+ ] );
128+ }
129+ else
130+ {
131+ $related_table_relations = isset( $related_table[ 'relations' ] ) && is_array( $related_table[ 'relations' ] )
132+ ? $related_table[ 'relations' ]
133+ : false;
134+
135+ $related_table_name = ! empty( $related_table[ 'table' ] ) ? $related_table[ 'table' ] : $related_field;
136+
137+ $relation_request = $this->airtable->getContent( "$related_table_name", [
138+ 'filterByFormula' => $relation_formula
139+ ], $related_table_relations );
140+ }
141+
142+
143+ $related_records = [];
144+
145+ do
146+ {
147+ $relation_response = $relation_request->getResponse();
148+
149+ if( ! is_array( $relation_response->records ) || count( $relation_response->records ) < 0 )
150+ {
151+ break;
152+ }
153+
154+ foreach ( $relation_response->records as $related_record )
155+ {
156+ $formatted_record = $related_record->fields;
157+ $formatted_record->id = $related_record->id;
158+
159+ $related_records[] = $formatted_record;
160+ }
161+ }
162+ while( $relation_request = $relation_response->next() );
163+
164+ if( is_array( $record->fields->$related_field ) )
165+ {
166+ $record->fields->$related_field = $related_records;
167+ }
168+ else
169+ {
170+ $record->fields->$related_field = count( $related_records ) > 0
171+ ? $related_records[ 0 ]
172+ : null;
173+ }
174+
175+ return $record;
176+
177+ }
178+
179+ public function next()
180+ {
181+
182+ if( ! $this->parsedContent )
183+ {
184+ return false;
185+ }
186+
187+
188+ if( ! isset( $this[ 'offset' ] ) )
189+ {
190+ return false;
191+ }
192+
193+ $this->request->offset = $this[ 'offset' ];
194+
195+ return $this->request;
196+
197+ }
198+
199+ public function __get( $key )
200+ {
201+ if( ! $this->parsedContent || ! isset( $this->parsedContent->$key ) )
202+ {
203+ return null;
204+ }
205+
206+ return $this->parsedContent->$key;
207+ }
208+
209+ public function __toString()
210+ {
211+ return $this->content;
212+ }
213+
214+ public function __isset( $key )
215+ {
216+ return $this->parsedContent && isset( $this->parsedContent->$key );
217+ }
218+
219+ public function offsetExists($offset)
220+ {
221+ return $this->parsedContent && isset( $this->parsedContent->$offset );
222+ }
223+
224+ public function offsetGet($offset)
225+ {
226+ return $this->parsedContent && isset( $this->parsedContent->$offset )
227+ ? $this->parsedContent->$offset
228+ : null;
229+ }
230+
231+ public function offsetSet($offset, $value)
232+ {
233+ if( $this->parsedContent )
234+ {
235+ $this->parsedContent->$offset = $value;
236+ }
237+ }
238+
239+ public function offsetUnset($offset)
240+ {
241+ if( $this->parsedContent && isset( $this->parsedContent->$offset ) )
242+ {
243+ unset( $this->parsedContent->$offset );
244+ }
245+ }
246+}
templates/.gitignorenew file+2
@@ -0,0 +1,2 @@
1+# OS
2+.DS_Store
templates/index.html+1
@@ -1,6 +1,7 @@
1{% extends "base.html" %}
1{% extends "base.html" %}
2{% block title %} {{ section.title }} {% endblock title %}
2{% block title %} {{ section.title }} {% endblock title %}
3{% block content %}
3{% block content %}
4+{% include "partials/top-alert.html" %}
4<div class="wrapper">
5<div class="wrapper">
5 <div class="container">
6 <div class="container">
6 <div class="no-side-nav-column">
7 <div class="no-side-nav-column">
templates/page.html−24
@@ -8,30 +8,6 @@
8 <div class="wrapper">
8 <div class="wrapper">
9 <div class="container container--fixed content-nav-container">
9 <div class="container container--fixed content-nav-container">
10 <div id="js-content-nav-toggle" class="content-nav-icon"></div>
10 <div id="js-content-nav-toggle" class="content-nav-icon"></div>
11− <nav class="content-nav">
12− <ul class="list">
13− <li class="list__item content-nav__item">
14− <a href="{{ config.base_url }}/posts/">
15− All
16− </a>
17− </li>
18− <li class="list__item content-nav__item">
19− <a href="{{ config.base_url }}/posts/essays/">
20− Essays
21− </a>
22− </li>
23− <li class="list__item content-nav__item">
24− <a href="{{ config.base_url }}/posts/media/">
25− Media
26− </a>
27− </li>
28− <li class="list__item content-nav__item">
29− <a href="{{ config.base_url }}/posts/updates/">
30− Updates
31− </a>
32− </li>
33− </ul>
34− </nav>
35 <div class="content-spacer"></div>
11 <div class="content-spacer"></div>
36 </div>
12 </div>
37 <div class="container">
13 <div class="container">
templates/partials/navigation-special.htmlnew file+60
@@ -0,0 +1,60 @@
1+{% block content %}
2+<header id="main-nav" class="header">
3+ <div class="wrapper">
4+ <div id="js-main-nav-toggle" class="header__nav-icon"></div>
5+ </div>
6+ <nav class="header__navigation">
7+ <div class="wrapper">
8+ <div class="container">
9+ <div class="nav-column">
10+ <div class="nav-links mb24">
11+ <ul class="list">
12+ {% for item in config.extra.urbit_menu %}
13+ <li class="list__item">
14+ <h2 class="mt0 mb6">
15+ <a href="{{ item.url | replace(from="$BASE_URL", to=config.base_url) }}">
16+ {{ item.name }}
17+ </a>
18+ </h2>
19+ </li>
20+ {% endfor %}
21+ </ul>
22+ </div>
23+ <section class="mt9 mb6">
24+ <h4 class="mb1">Follow</h4>
25+ <ul class="list">
26+ <li class="list__item">
27+ <a class="mono" href="#">twitter.com/urbit</a>
28+ </li>
29+ <li class="list__item">
30+ <a class="mono" href="#">github.com/urbit</a>
31+ </li>
32+ </ul>
33+ </section>
34+ <section class="mb6">
35+ <h4 class="mb1">Contact</h4>
36+ <ul class="list">
37+ <li class="list__item">
38+ <a class="mono" href="mailto:support@urbit.org">support@urbit.org</a>
39+ </li>
40+ </ul>
41+ </section>
42+ <section class="mb6">
43+ <h4 class="mb1">Company</h4>
44+ <ul class="list">
45+ {% for item in config.extra.urbit_footer %}
46+ <li class="list__item">
47+ <a class="mono" href="{{ item.url | replace(from="$BASE_URL", to=config.base_url) }}">
48+ {{ item.name }}
49+ </a>
50+ </li>
51+ {% endfor %}
52+ </ul>
53+ </section>
54+ </div>
55+ </div>
56+ </div>
57+ </nav>
58+ </div>
59+</header>
60+{% endblock content %}
templates/partials/top-alert.htmlnew file+9
@@ -0,0 +1,9 @@
1+{% block content %}
2+<!-- <div class="bg-black z3 w-full mb4">
3+ <div class="top-bar-inner">
4+ <p class="white fs35 mv0">
5+ Interested in developing on Urbit? <a href="/hoonschool" class="white">Sign up for Hoon School!</a>
6+ </p>
7+ </div>
8+</div> -->
9+{% endblock content %}
templates/rss.xmlnew file+21
@@ -0,0 +1,21 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
3+ <channel>
4+ <title>{{ config.title }}</title>
5+ <link>{{ config.base_url | safe }}</link>
6+ <description>{{ config.description }}</description>
7+ <generator>Zola</generator>
8+ <language>{{ config.default_language }}</language>
9+ <atom:link href="{{ feed_url | safe }}" rel="self" type="application/rss+xml"/>
10+ <lastBuildDate>{{ last_build_date | date(format="%a, %d %b %Y %H:%M:%S %z") }}</lastBuildDate>
11+ {% for page in pages %}
12+ <item>
13+ <title>{{ page.title }}</title>
14+ <pubDate>{{ page.date | date(format="%a, %d %b %Y %H:%M:%S %z") }}</pubDate>
15+ <link>{{ page.permalink | safe }}</link>
16+ <guid>{{ page.permalink | safe }}</guid>
17+ <description>{% if page.summary %}{{ page.summary }}{% else %}{{ page.content }}{% endif %}</description>
18+ </item>
19+ {% endfor %}
20+ </channel>
21+</rss>
templates/sections/index.htmlnew file+46
@@ -0,0 +1,46 @@
1+{% extends "base.html" %}
2+
3+{% block content %}
4+<div class="wrapper">
5+ <div class="container">
6+ <div class="no-side-nav-column">
7+ <h1 class="mt10">Urbit</h1>
8+ <p class="mb6">A personal server built from scratch.</p>
9+ <ul class="list">
10+ <li class="list__item">
11+ <h2>
12+ <a href="{{config.base_url ~ "/primer/"}}">
13+ Primer
14+ </a>
15+ </h2>
16+ <p class="mb6">
17+ A 10-minute read on what Urbit is and why it exists.
18+ </p>
19+ </li>
20+ <li class="list__item">
21+ <h2>
22+ <a href="{{config.base_url ~ "/posts/"}}">
23+ Posts
24+ </a>
25+ </h2>
26+ <p class="mb6">
27+ Regular updates, occasional essays and media.
28+ </p>
29+ </li>
30+ <li class="list__item">
31+ <h2>
32+ <a href="{{config.base_url ~ "/docs/"}}">
33+ Docs
34+ </a>
35+ </h2>
36+ <p class="mb6">
37+ Installation instructions and complete system documentation.
38+ </p>
39+ </li>
40+ </ul>
41+ <!-- footer -->
42+ {% include "partials/footer.html" %}
43+ </div>
44+ </div>
45+</div>
46+{% endblock content %}