@@ -6,28 +6,26 @@ template = "doc.html"
66insert_anchor_links = "right"
77+++
88
9−
10−
11−Library is a sample social media application in which you can create a collection of books, called a library, which can contain any number of books.
12−You can share individual collections per-ship. The creator of the collection has de-facto admin powers; he is the only one who can add or remove books to/from the library,
13−remove the library itself, and add comments or remove anyone's comments. Guest ships may request access to specific libraries, which if granted,
9+Library is a sample social media application in which you can create a collection of books, called a library, which can contain any number of books.
10+You can share individual collections per-ship. The creator of the collection has de-facto admin powers; he is the only one who can add or remove books to/from the library,
11+remove the library itself, and add comments or remove anyone's comments. Guest ships may request access to specific libraries, which if granted,
1412allows them to request any book from the library.
1513
1614Using this application, you can:
1715
18−- Create libraries, where a library is a collection of books
19−- Add and remove books from a library, if you are the owner
20−- Allow others to view your library based on various permissioning schemes (policies)
21−- Add and remove comments from a library, if you are the owner or were granted access
16+- Create libraries, where a library is a collection of books
17+- Add and remove books from a library, if you are the owner
18+- Allow others to view your library based on various permissioning schemes (policies)
19+- Add and remove comments from a library, if you are the owner or were granted access
2220
2321## Prerequisites
2422
25−This overview is a companion document to the *Library* application, and assumes you are familiar with `%graph-store`. Before continuing, **you must familiarize yourself with the sample application**, which can be found [here](https://github.com/ynx0/library). See the [detailed usage guide](https://github.com/ynx0/library/blob/master/README.md) for download and installation instructions.
26−
23+This overview is a companion document to the _Library_ application, and assumes you are familiar with `%graph-store`. Before continuing, **you must familiarize yourself with the sample application**, which can be found [here](https://github.com/ynx0/library). See the [detailed usage guide](https://github.com/ynx0/library/blob/master/README.md) for download and installation instructions.
2724
2825## Project Structure
2926
3027Here is the directory structure of our app.
28+
3129```
3230├── app
3331│ └── library-proxy.hoon
@@ -58,29 +56,28 @@ Here is the directory structure of our app.
5856
5957- [`install.sh`](https://github.com/ynx0/library/blob/master/install.sh) is a script that automates copying the source files into a ship's pier / home desk.
6058
61−
6259## Implementation Summary
6360
6461The project contains a custom `%graph-store` validator tht specifies the application's schema and a custom gall agent, called `%library-proxy`, which sends `%graph-store` updates between the host ship and subscribers.
65−The gall agent also generates `%graph-store` updates based on commands and actions that a user issues. Commands can only be issued to a proxy by the ship owner,
66−while actions can be issued by any ship, but may fail based on permissions. A host ship's `%library-proxy` talks to the subscriber ship's `%library-proxy` through the normal gall app channels (pokes/peeks)
67−to send them any updates that have taken place on the library graph. It is the host's responsibility to forward all relevant updates to subscribers
62+The gall agent also generates `%graph-store` updates based on commands and actions that a user issues. Commands can only be issued to a proxy by the ship owner,
63+while actions can be issued by any ship, but may fail based on permissions. A host ship's `%library-proxy` talks to the subscriber ship's `%library-proxy` through the normal gall app channels (pokes/peeks)
64+to send them any updates that have taken place on the library graph. It is the host's responsibility to forward all relevant updates to subscribers
6865(kept track of in a data structure defined [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/sur/library.hoon#L14-L15)),
6966while subscribers must only trust graph updates that it receives from the owner of that resource.
7067
7168Here are the proxy's responsibilities:
7269
73−- **Application-specific API** - presents an interface for a user to interact with both his/her own library proxy and others' by directly defining and implementing a user-facing API as pokes and scries,
74− as opposed to forcing the user to deal with the graph-store API directly. The `command`/`action`/`response` poke types are defined [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/sur/library.hoon#L17-L42),
75− while the scries are defined [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L132).
76−- **Graph store update creation** - creates the appropriate graph update for a given user-facing action
77−- **Graph store update proxying** - handles the networking and subscription logic required to send graph store updates between host and subscriber
78−- **Access control** - allows or denies ships access to libraries, checks for proper permissions before processing a `command` or `action`
70+- **Application-specific API** - presents an interface for a user to interact with both his/her own library proxy and others' by directly defining and implementing a user-facing API as pokes and scries,
71+ as opposed to forcing the user to deal with the graph-store API directly. The `command`/`action`/`response` poke types are defined [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/sur/library.hoon#L17-L42),
72+ while the scries are defined [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L132).
73+- **Graph store update creation** - creates the appropriate graph update for a given user-facing action
74+- **Graph store update proxying** - handles the networking and subscription logic required to send graph store updates between host and subscriber
75+- **Access control** - allows or denies ships access to libraries, checks for proper permissions before processing a `command` or `action`
7976
8077## Interaction with `%graph-store`
8178
8279There are a couple of ways that our application uses and interacts with `%graph-store`.
83−Firstly, the app subscribes to `%graph-store` in `+on-init` on path `/updates`, meaning it will be notified of every single `graph-update` that occurs on the local ship. (*Recall the type of `graph-update`, defined [here](https://github.com/urbit/urbit/blob/ceed4b78d068d7cb70350b3cd04e7525df1c7e2d/pkg/arvo/sur/graph-store.hoon#L29), the reference for which can be found [here](https://urbit.org/docs/userspace/graph-store/data-structure-overview/#update-part-1)*). We also define a validator for our application that `%graph-store` uses for every *Library* graph. Since `%graph-store` registers all validators under the path `%/graph/validator/<name of app>/hoon`, we don't have to do anything special to register our validator. We just need to correctly name our file, and provide an `+graph-indexed-post` arm which should perform all schema validation.
80+Firstly, the app subscribes to `%graph-store` in `+on-init` on path `/updates`, meaning it will be notified of every single `graph-update` that occurs on the local ship. (_Recall the type of `graph-update`, defined [here](https://github.com/urbit/urbit/blob/ceed4b78d068d7cb70350b3cd04e7525df1c7e2d/pkg/arvo/sur/graph-store.hoon#L29), the reference for which can be found [here](https://urbit.org/docs/userspace/graph-store/data-structure-overview/#update-part-1)_). We also define a validator for our application that `%graph-store` uses for every _Library_ graph. Since `%graph-store` registers all validators under the path `%/graph/validator/<name of app>/hoon`, we don't have to do anything special to register our validator. We just need to correctly name our file, and provide an `+graph-indexed-post` arm which should perform all schema validation.
8481
8582### Synchronizing Graphs Between Ships
8683
@@ -88,7 +85,7 @@ An owner is responsible for forwarding any updates to clients. Whenever the `%li
8885
8986On the receiving end, since we know that we are not the source of the `graph-update`, we handle the update in the `handle-incoming-graph-store` helper arm, which makes sure to only process and forward graph store updates to local graph store that are sent by the owner, and no one else. Once it passes this permissions check, it is poked into the local graph like any other graph-update.
9087
91−\**One could imagine a gossip style protocol where the data is like a fact so it doesn't matter where it came from, but in this app the library host is defined as the source of truth.*
88+\*_One could imagine a gossip style protocol where the data is like a fact so it doesn't matter where it came from, but in this app the library host is defined as the source of truth._
9289
9390## Data Model
9491
@@ -99,7 +96,7 @@ meant to represent a comment on any given book.
9996
10097### Representing Data using `%graph-store`
10198
102−The data types we defined for our application do not fit within `%graph-store` out of the box. `%graph-store` doesn't allow arbitrarily typed data in a node's content field, so we create an ad-hoc representation that we can cleanly convert to and from our own data types and `%graph-store` types.
99+The data types we defined for our application do not fit within `%graph-store` out of the box. `%graph-store` doesn't allow arbitrarily typed data in a node's content field, so we create an ad-hoc representation that we can cleanly convert to and from our own data types and `%graph-store` types.
103100
104101Here is the conversion code: [(link)](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/lib/library.hoon#L12-L19)
105102
@@ -120,28 +117,28 @@ Each arm takes in either a `book` or a `comment`, our application specific data
120117
121118We define 3 core structures:
122119
123−- **Library** - The fundamental data structure. A library is a graph that contains books
124−- **Book** - A data structure that contains an entry of a given book's metadata (i.e. title and isbn) and any comments associated with it. It is represented by a top-level node within a library graph
125−- **Comment** - A node that represents a user's comment on a given book. Represented as a child node of a book's comment container node.
120+- **Library** - The fundamental data structure. A library is a graph that contains books
121+- **Book** - A data structure that contains an entry of a given book's metadata (i.e. title and isbn) and any comments associated with it. It is represented by a top-level node within a library graph
122+- **Comment** - A node that represents a user's comment on a given book. Represented as a child node of a book's comment container node.
126123
127124We then define the layout of the data as follows:
128125
129−* Library
130− - Book
131− + Metadata Revision Container
132− * Specific Metadata Revision
133− + Comments Container
134− * Specific Comment
126+- Library
127+ - Book
128+ - Metadata Revision Container
129+ - Specific Metadata Revision
130+ - Comments Container
131+ - Specific Comment
135132
136−Every graph created with the `%graph-validator-library` is a *Library* graph, and thus it has the following characteristics:
133+Every graph created with the `%graph-validator-library` is a _Library_ graph, and thus it has the following characteristics:
137134
138−- Every Library graph represents a library, or a collection of books
139−- Every top level node in a library represents a book and all related contents
140−- Within a book there are always two structural nodes: a container node for metadata revisions, and a container node that holds all comments, modeling a comments section
135+- Every Library graph represents a library, or a collection of books
136+- Every top level node in a library represents a book and all related contents
137+- Within a book there are always two structural nodes: a container node for metadata revisions, and a container node that holds all comments, modeling a comments section
141138
142139To make things more concrete, let's look at an example of an actual graph.
143140
144−(*For the sake of clarity, we have replaced each index with its representation in its original aura in following output.*)
141+(_For the sake of clarity, we have replaced each index with its representation in its original aura in following output._)
145142
146143```
147144[ p=[entity=~zod name=%library1]
@@ -261,17 +258,17 @@ To make things more concrete, let's look at an example of an actual graph.
261258]
262259```
263260
264−
261+
265262
266−* The above graph represents a library that has a single book, whose metadata is stored under the `%meta` revision container. The metadata associated with this specific book entry is currently with title: `"Dune"` and isbn `"0441172717"`.
263+- The above graph represents a library that has a single book, whose metadata is stored under the `%meta` revision container. The metadata associated with this specific book entry is currently with title: `"Dune"` and isbn `"0441172717"`.
267264 The reason we have a revision container for the book metadata is so that in case someone makes an error, they may correct it. A potential frontend could then simply show the most recent version.
268−* Each comment is simply a node under a book's `%comment` node with a single `%text` content.
265+- Each comment is simply a node under a book's `%comment` node with a single `%text` content.
269266 We skipped revision containers for comments to keep schema simple, but you could imagine duplicating the same logic for comments as well.
270−* Every book node has an index the datetime of when it was created.
271−* Each structural node in a book has a constant index fragment, either `%meta` for the metadata revision container node,
267+- Every book node has an index the datetime of when it was created.
268+- Each structural node in a book has a constant index fragment, either `%meta` for the metadata revision container node,
272269 or `%comments` for the comments container node.
273−* Every comment has an index of the datetime of when it was posted.
274−* The metadata revisions are a single incrementing number starting from 1, so the initial metadata has a revision count of `1`, and a subsequent edit has a revision count of `2`, and so on.
270+- Every comment has an index of the datetime of when it was posted.
271+- The metadata revisions are a single incrementing number starting from 1, so the initial metadata has a revision count of `1`, and a subsequent edit has a revision count of `2`, and so on.
275272
276273### Schema Enforcement
277274
@@ -280,12 +277,12 @@ Take a moment to read through it and cross-check your understanding with the fol
280277
281278**Validator logic summary**
282279
283−- None of the structural nodes require data, so we simply assert that their contents are empty
284−- For a *Specific Comment Node*, a post with an index matching the structure `[@ %comments @ ~]`, we assert that `contents` only contains a single `%text` content.
285− This choice is arbitrary but meant to keep the application simple and so that a potential frontend does not have to do complex rendering.
286−- For a *Specific Metadata Revision*, a post with index matching the structure `[@ %meta @ ~]`, we first ensure that it's contents only contain two `%text` content instances.
287− We then use two helper arms `+is-title-valid` and `+is-isbn-valid` to validate the two values. For us, any title value is accepted, while only strings with length 10 or 13 are valid ISBNs.
288− (We don't do any true ISBN validation for simplicity's sake)
280+- None of the structural nodes require data, so we simply assert that their contents are empty
281+- For a _Specific Comment Node_, a post with an index matching the structure `[@ %comments @ ~]`, we assert that `contents` only contains a single `%text` content.
282+ This choice is arbitrary but meant to keep the application simple and so that a potential frontend does not have to do complex rendering.
283+- For a _Specific Metadata Revision_, a post with index matching the structure `[@ %meta @ ~]`, we first ensure that it's contents only contain two `%text` content instances.
284+ We then use two helper arms `+is-title-valid` and `+is-isbn-valid` to validate the two values. For us, any title value is accepted, while only strings with length 10 or 13 are valid ISBNs.
285+ (We don't do any true ISBN validation for simplicity's sake)
289286
290287## Access Control
291288
@@ -296,6 +293,7 @@ which are set by the user per-library at the time of creation (stored [here](htt
296293These specify who can or cannot gain access to a library.
297294
298295Here's what `policy` looks like:
296+
299297```
300298+$ policy
301299 $% [%open ~] :: any ship is allowed
@@ -307,13 +305,14 @@ Here's what `policy` looks like:
307305There are also implicit rules, defined as follows:
308306
309307For any given library that one owns,
308+
310309- An owner can:
311− + add a top level book node to any library
312− + add a metadata revision to any book
313− + add or remove any comment on any book
310+ - add a top level book node to any library
311+ - add a metadata revision to any book
312+ - add or remove any comment on any book
314313- A reader, that is, a another ship granted access to the library, can:
315− + add a comment to any book that they have requested
316− + remove any comment that they are the author of
314+ - add a comment to any book that they have requested
315+ - remove any comment that they are the author of
317316
318317Implicitly, all readers are given permission to get any book when granted access to the library.
319318
@@ -323,6 +322,7 @@ Implicitly, all readers are given permission to get any book when granted access
323322
324323- The `+is-allowed` arm, (found [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/lib/library.hoon#L21))
325324 implements each `policy`'s behavior, and is reproduced below.
325+
326326 ```
327327 ++ is-allowed
328328 |= [requester=ship host=ship =policy:library]
@@ -335,8 +335,8 @@ Implicitly, all readers are given permission to get any book when granted access
335335 %whitelist (~(has in ships.policy) requester)
336336 ==
337337 ```
338− Given the host ship of a resource and the policy to be enforced for a resource, `+is-allowed` returns a boolean for whether or not a requesting ship should be allowed access to that resource.
339338
339+ Given the host ship of a resource and the policy to be enforced for a resource, `+is-allowed` returns a boolean for whether or not a requesting ship should be allowed access to that resource.
340340
341341- `+is-allowed` is used in `+on-watch` [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L113), where it only allows a ship to subscribe to a library if it passes the permissions check.
342342- It is also used [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L299) and [here](https://github.com/ynx0/library/blob/9104145bb24e8ec949a5e9685139665b07161dd6/app/library-proxy.hoon#L310), so that when a ship wants to know about what libraries and books exist, only data they are allowed to see gets revealed to them.
@@ -354,7 +354,7 @@ The permissioning scheme was also implemented relatively simple; it has some lim
354354### Comparison between `%library-proxy` and `%graph-push-hook`
355355
356356`%library-proxy` performs the functions of both `%graph-push-hook` and `%graph-pull-hook`.
357−`%graph-pull-hook` is responsible for asking other `%graph-push-hook`s for graph data
357+`%graph-pull-hook` is responsible for asking other `%graph-push-hook`s for graph data
358358by trying to initiate a subscription on the host ship's `%graph-push-hook`, which the host only allows if they have permissions.
359359When it succeeds, `%graph-push-hook` then sends out all `graph-updates` to those subscribers who need to hear about it.
360360`%graph-pull-hook` then merges in graph updates it hears into the local `%graph-store`.