diff --git a/.cspell/template-words.txt b/.cspell/template-words.txt
index 5868e11a1..a0f1fcd38 100644
--- a/.cspell/template-words.txt
+++ b/.cspell/template-words.txt
@@ -15,5 +15,5 @@ pagefind
rocketship
renderlayoutblock
srcset
-storysequence
+storystep
surpress
diff --git a/11ty-extensions/markdown-it-config.js b/11ty-extensions/markdown-it-config.js
index a49545bc5..c43eabaea 100644
--- a/11ty-extensions/markdown-it-config.js
+++ b/11ty-extensions/markdown-it-config.js
@@ -87,9 +87,9 @@ export default function(eleventyConfig) {
//Configure markdown-it plugins
mdLib.use(markdownItAttrs);
mdLib.use(markdownItAnchor, { tabIndex: false, slugify: s => slugify(s) });
- mdLib.use(markdownItContainer, "coreconcepts-intro");
- mdLib.use(markdownItContainer, "coreconcepts-orientation");
- mdLib.use(markdownItContainer, "coreconcepts-storysequence");
+ mdLib.use(markdownItContainer, "intro");
+ mdLib.use(markdownItContainer, "orientation");
+ mdLib.use(markdownItContainer, "storystep");
mdLib.use(markdownItContainer, "h-author");
mdLib.use(markdownItContainer, "output-block");
diff --git a/11ty-markdown-changes.md b/11ty-markdown-changes.md
index b729b1e98..4a173d67f 100644
--- a/11ty-markdown-changes.md
+++ b/11ty-markdown-changes.md
@@ -11,9 +11,9 @@
## Divs
- No more `markdown=1`, instead leave a blank line after.
- - coreconcepts-intro, coreconcepts-orientation, and coreconcepts-storysequence divs have been created as MD containers. Use:
+ - Core Concepts intro and orientation divs have had their `coreconcepts-` prefixes removed and can now be created as MD containers. `coreconcepts-storysequence` blocks have been replaced with individual `storystep` blocks around each step in a story sequence. Use:
```
- ::: coreconcepts-intro
+ ::: intro
words words
:::
```
diff --git a/README.md b/README.md
index 7adce323f..8b5fd0b7f 100644
--- a/README.md
+++ b/README.md
@@ -72,7 +72,7 @@ Additionally the following `markdown-it` plugins have been added:
There are of course more details. See the docs at [markdown-it-attrs](https://github.com/arve0/markdown-it-attrs) for more.
- [markdown-it-container](https://github.com/markdown-it/markdown-it-container) Plugin for creating block-level custom containers. For example:
```
- ::: coreconcepts-intro
+ ::: intro
Blah blah
:::
```
@@ -80,7 +80,7 @@ Additionally the following `markdown-it` plugins have been added:
renders as
```
-
+
```
@@ -88,10 +88,10 @@ Additionally the following `markdown-it` plugins have been added:
Each container type needs to be configured in `markdown-it-config.js` for examples and to add more. This one provides a lot of flexibility.
## Site search and indexing
-The site uses the [Pagefind](https://pagefind.app/) library to index the contents of the site and to find search results upon request.
-The documentation is quite good. One thing to note; pages are indexed based on the inclusion of the `data-pagefind-body`
-attribute on a page. It is included on all of the main pages (all but the Design System). If you need to remove some or all of a page
-from indexing you can use the `data-pagefind-ignore` attribute. See (https://pagefind.app/docs/indexing/) for details.
+The site uses the [Pagefind](https://pagefind.app/) library to index the contents of the site and to find search results upon request.
+The documentation is quite good. One thing to note; pages are indexed based on the inclusion of the `data-pagefind-body`
+attribute on a page. It is included on all of the main pages (all but the Design System). If you need to remove some or all of a page
+from indexing you can use the `data-pagefind-ignore` attribute. See (https://pagefind.app/docs/indexing/) for details.
## Setup for Dev
- `npm install`
diff --git a/src/custom/style.css b/src/custom/style.css
index 7ba4e6704..c327009db 100644
--- a/src/custom/style.css
+++ b/src/custom/style.css
@@ -149,21 +149,21 @@ article img { display: table; margin-left: auto; margin-right: auto; }
/* Core Concepts */
-.md-typeset .coreconcepts-intro { font-size: .98rem; border-left: .2rem solid rgba(0,0,0,.2); padding-left: 0.6rem; font-weight: 500; line-height: 1.55; }
-.md-typeset .coreconcepts-intro p { letter-spacing: -.3px; }
-.md-typeset .coreconcepts-orientation { margin: 2.2rem 0; padding: 1.6rem 1.2rem 1.8rem; background: #F8F8F8; position: relative; }
-.md-typeset .coreconcepts-orientation :first-child { margin-top: 0; }
-.md-typeset .coreconcepts-orientation :last-child { margin-bottom: 0; }
-.md-typeset .coreconcepts-orientation h2, .md-typeset .coreconcepts-orientation h3 { position: relative; }
-.md-typeset .coreconcepts-orientation a { font-weight: 500; letter-spacing: -.3px; }
-.md-typeset .coreconcepts-orientation i { position: absolute; left: -40px; top: 13px; font-size: 28px; color: #3915ad; }
-.md-typeset .coreconcepts-orientation ol { margin-bottom: -12px; margin-left: 0; }
-.md-typeset .coreconcepts-storysequence > ol { margin-left: 0; }
-.md-typeset .coreconcepts-storysequence > ol > li { list-style: none; padding: 0.6rem; background: #F8F8F8; margin: 1rem 0; }
-.md-typeset .coreconcepts-storysequence > ol > li > img { margin-bottom: 1rem; }
+.md-typeset .intro { font-size: .98rem; border-left: .2rem solid rgba(0,0,0,.2); padding-left: 0.6rem; font-weight: 500; line-height: 1.55; }
+.md-typeset .intro p { letter-spacing: -.3px; }
+.md-typeset .orientation { margin: 2.2rem 0; padding: 1.6rem 1.2rem 1.8rem; background: #F8F8F8; position: relative; }
+.md-typeset .orientation :first-child { margin-top: 0; }
+.md-typeset .orientation :last-child { margin-bottom: 0; }
+.md-typeset .orientation h2, .md-typeset .orientation h3 { position: relative; }
+.md-typeset .orientation a { font-weight: 500; letter-spacing: -.3px; }
+.md-typeset .orientation i { position: absolute; left: -40px; top: 13px; font-size: 28px; color: #3915ad; }
+.md-typeset .orientation ol { margin-bottom: -12px; margin-left: 0; }
+.md-typeset .storysequence > ol { margin-left: 0; }
+.md-typeset .storysequence > ol > li { list-style: none; padding: 0.6rem; background: #F8F8F8; margin: 1rem 0; }
+.md-typeset .storysequence > ol > li > img { margin-bottom: 1rem; }
@media (max-width: 1600px) {
- .md-typeset .coreconcepts-orientation i { left: -36px; top: 12px; font-size: 26px; }
+ .md-typeset .orientation i { left: -36px; top: 12px; font-size: 26px; }
}
/* Tables */
diff --git a/src/pages/build/guide/index.md b/src/pages/build/guide/index.md
index 8f162b9db..8affb404f 100644
--- a/src/pages/build/guide/index.md
+++ b/src/pages/build/guide/index.md
@@ -2,7 +2,7 @@
title: Holochain Build Guide
---
-::: coreconcepts-intro
+::: intro
This Build Guide organizes everything you need to know about developing Holochain applications into individual topics. Each topic page stands alone as a comprehensive guide to using a given feature or implementing a given functionality. There are lots of code examples which make it clear how to do something yet are generic enough to be universally useful. These examples may not cover every single use case, though, so we'll point you to the reference documentation often.
:::
diff --git a/src/pages/concepts/10_countersigning.md b/src/pages/concepts/10_countersigning.md
index 92d1ab81b..50f91b31c 100644
--- a/src/pages/concepts/10_countersigning.md
+++ b/src/pages/concepts/10_countersigning.md
@@ -2,11 +2,11 @@
title: "Countersigning: Reaching Agreement Between Peers"
---
-::: coreconcepts-intro
+::: intro
Although there's no global consensus in a Holochain application, it's sometimes useful for two or more parties to record shared agreements. Countersigning allows them to coordinate the writing of a single entry across all their source chains.
:::
-::: coreconcepts-orientation
+::: orientation
### What you'll learn
1. [Differences between Holochain, client/server, and blockchain](#agreement-versus-consensus)
@@ -45,14 +45,19 @@ In order to safely coordinate the writing of a single new action to both of thei
Here's a more in-depth look at this process. Keep in mind that it's meant to be an automated process, finished within a few seconds while Alice and Bob are both online. Any human agreement (such as negotiating meeting times, contract terms, or transaction amount) should happen beforehand.
-::: coreconcepts-storysequence
-
+::: storystep
![](/assets/img/concepts/10.2-entry-exchange.png){.sz80p} {.center}
+---
+
Alice and Bob exchange the details needed to create the entry that will be countersigned.
+:::
+::: storystep
![](/assets/img/concepts/10.3-preflight.png){.sz80p} {.center}
+---
+
Alice creates a **preflight request** containing:
* The parties involved (her and Bob in this case)
@@ -60,49 +65,89 @@ Alice creates a **preflight request** containing:
* The time window in which the countersigning must complete
* A stub of the action to go along with the entry, which contains its action type and entry type (currently create-entry and update-entry actions can be countersigned; in the future delete-entry, create-link, and delete-link actions will be too)
* Optional arbitrary application data
+:::
+::: storystep
![](/assets/img/concepts/10.4-preflight-send.png){.sz80p} {.center}
+---
+
Alice sends the preflight request to Bob for approval. (You can implement this any way you like, but a [remote call](../8_calls_capabilities/) is probably the best.) At this stage, it's expected that Bob is able to independently construct the data that will go into the countersigned entry, based perhaps on his history with Alice or the optional application data, to determine whether the request is something he wants to accept. Acceptance could be automated, or it could request Bob's intervention via a signal to his UI.
+:::
+::: storystep
![](/assets/img/concepts/10.5-preflight-inspection.png){.sz80p} {.center}
+---
+
Bob's conductor attempts to accept the preflight request, which involves checking whether it is possible to write a countersigned record (that is, there are no incomplete countersigning sessions or pending source chain writes, and Alice's specified time window doesn't conflict with Bob's system clock). If everything looks good, Bob's conductor locks his source chain.
+:::
+::: storystep
![](/assets/img/concepts/10.6-bob-lock.png){.sz80p} {.center}
+---
+
Bob's conductor produces a **preflight response**, containing his signature on the request and the record ID at which his source chain is currently locked. Bob sends this preflight response back to Alice.
+:::
+::: storystep
![](/assets/img/concepts/10.7-alice-lock.png){.sz80p} {.center}
+---
+
Alice receives Bob's preflight response, then tries to generate a preflight response herself from the request she created. Now her own source chain is locked as well.
+:::
+::: storystep
![](/assets/img/concepts/10.8-countersigning-session.png){.sz80p} {.center}
+---
+
Alice compiles both the preflight responses into a **countersigning session** structure, containing the original agreed-upon preflight request and all the responses. She shares it with Bob (again, this could be done with a remote call).
+:::
+::: storystep
![](/assets/img/concepts/10.9-trial-commit.png){.sz80p} {.center}
+---
+
Alice and Bob both create the entry to be countersigned on their own machines, which contains both the countersigning session data structure and the app data they'd previously agreed on. Then they do a trial run of committing the entry.
+:::
+::: storystep
![](/assets/img/concepts/10.10-validate.png){.sz80p} {.center}
+---
+
At this point Alice and Bob's conductors take over completely from the cells. As with any commit, they attempt to validate the new action. But with a countersigned action, each participant's conductor validates it multiple times --- once from their own perspective, and once from the perspective of all the other parties. This is possible because Alice and Bob both have the same countersigning session data structure, which contains the current states of both of their source chains. If this step didn't happen, DHT validating authorities would [warrant](../7_validation/#invalid-entry) all parties for neglect, not just the one for whom the action was invalid.
+:::
+::: storystep
![](/assets/img/concepts/10.11-authorities-collect.png){.sz80p} {.center}
+---
+
Once validation is finished, Alice and Bob both do a trial run of publishing the operations generated by the new action to the DHT --- but only the [store entry operations](../4_dht/#a-cloud-of-witnesses). The authorities collect the data but don't integrate it into their DHT stores; right now they're simply acting as witnesses who wait until they've seen the operations from both Alice and Bob.
+:::
+::: storystep
![](/assets/img/concepts/10.12-authorities-distribute.png){.sz80p} {.center}
+---
+
Once the authorities have collected all the data, they send the full set of actions back to both of them.
+:::
!!! info Timeouts
If Alice and Bob reach the end of the time window without receiving this action set, their conductors discard the new action and unlock their source chains as if nothing had happened. Likewise, the authorities discard the pre-published data if they don't collect all the required signatures within the time window. There is no built-in feature to retry an expired session, but it could be built into the application.
!!!
+::: storystep
![](/assets/img/concepts/10.13-commit-and-publish.png){.sz80p} {.center}
+---
+
When Alice and Bob's conductors each receive the full set of actions, they finally unlock their source chains, commit the actions, and publish _all_ the resulting operations to the DHT as normal. This creates a permanent, public record of all parts of the transaction.
:::
diff --git a/src/pages/concepts/11_lifecycle_events.md b/src/pages/concepts/11_lifecycle_events.md
index c4a171f71..fb206a30d 100644
--- a/src/pages/concepts/11_lifecycle_events.md
+++ b/src/pages/concepts/11_lifecycle_events.md
@@ -2,11 +2,11 @@
title: "Lifecycle events: reacting to external triggers"
---
-::: coreconcepts-intro
+::: intro
In the course of its existence, the conductor triggers **lifecycle events**, to which a cell can respond with specially named callbacks.
:::
-::: coreconcepts-orientation
+::: orientation
### What you'll learn
* [How to tell the conductor about the entry types your zome defines](#entry-type-definition-callbacks)
diff --git a/src/pages/concepts/1_the_basics.md b/src/pages/concepts/1_the_basics.md
index 64e63858d..c8204a5ae 100644
--- a/src/pages/concepts/1_the_basics.md
+++ b/src/pages/concepts/1_the_basics.md
@@ -2,11 +2,11 @@
title: "Holochain Core Concepts: What is Holochain?"
---
-::: coreconcepts-intro
+::: intro
Holochain is an open-source application development framework and peer-to-peer networking protocol. It allows you to create **actually serverless applications** with high levels of **security, reliability, and performance**. Every participant runs the application on their own device, creates and stores their own data, and talks directly to other participants. The security of the application is supported by both cryptography and peer accountability.
:::
-::: coreconcepts-orientation
+::: orientation
###
What you'll learn
1. [The problem with centralized architectures](#the-problem-with-centralized-architectures)
diff --git a/src/pages/concepts/2_application_architecture.md b/src/pages/concepts/2_application_architecture.md
index 325a1a3ad..60aa8a8c8 100644
--- a/src/pages/concepts/2_application_architecture.md
+++ b/src/pages/concepts/2_application_architecture.md
@@ -2,10 +2,10 @@
title: Application Architecture
---
-::: coreconcepts-intro
+::: intro
Applications built with Holochain are highly **modular**. This makes it easy to share code and [compose](https://en.wikipedia.org/wiki/Composability) smaller pieces together into larger wholes. Each functional part of a Holochain application, called a **DNA**, has its own application logic, isolated peer-to-peer network, and shared database.
-::: coreconcepts-orientation
+::: orientation
###
What you'll learn
1. [Agent-centricity](#agent-centricity)
diff --git a/src/pages/concepts/3_source_chain.md b/src/pages/concepts/3_source_chain.md
index 3898882bf..8248c74e7 100644
--- a/src/pages/concepts/3_source_chain.md
+++ b/src/pages/concepts/3_source_chain.md
@@ -2,11 +2,11 @@
title: "The Source Chain: A Personal Data Journal"
---
-::: coreconcepts-intro
+::: intro
Each participant in a Holochain network creates and stores their own data in a journal called a **source chain**. Each journal entry is cryptographically signed by its author and is immutable once written.
:::
-::: coreconcepts-orientation
+::: orientation
###
What you'll learn
1. [How agent identities are created](#agent-identity)
diff --git a/src/pages/concepts/4_dht.md b/src/pages/concepts/4_dht.md
index 074297e6d..d0b493aea 100644
--- a/src/pages/concepts/4_dht.md
+++ b/src/pages/concepts/4_dht.md
@@ -2,11 +2,11 @@
title: "The DHT: A Shared, Distributed Graph Database"
---
-::: coreconcepts-intro
+::: intro
Agents share records of their actions, including any data meant to be shared with the group, in a [**distributed hash table (DHT)**](https://en.wikipedia.org/wiki/Distributed_hash_table). This database provides redundancy and availability for data and gives the network the power to detect and take action against corruption.
:::
-::: coreconcepts-orientation
+::: orientation
###
What you'll learn
1. [The downsides and risks of self-owned data](#self-owned-data-isn-t-enough)
@@ -65,32 +65,52 @@ Every peer knows about all of its closest neighbors and a few faraway acquaintan
Let's see how this works with a very small address space. Instead of public keys and hashes, we're just going to use letters of the alphabet, where the first letter of a word is its address.
-::: coreconcepts-storysequence
-
+::: storystep
![](/assets/img/concepts/4.5-alice-neighborhood.png){.sz80p} {.center}
+---
+
Alice lives at address A. Her neighbors to the left are Diana and Fred, and her neighbors to the right are Zoƫ and Walter.
+:::
+::: storystep
![](/assets/img/concepts/4.7-alice-publish-address-calculation.png){.sz80p} {.center}
+---
+
Alice creates an entry containing the word "molecule", whose address is M.
+:::
+::: storystep
![](/assets/img/concepts/4.8-authority-resolution.png){.sz80p} {.center}
+---
+
Of all of Alice's neighbors, Fred is closest to that address, so she asks him to store it. Fred hasn't claimed authority for that address, so he tells Alice about his neighbor Louise.
+:::
+::: storystep
![](/assets/img/concepts/4.9-gossip-publish.png){.sz80p} {.center}
+---
+
Alice shares the entry with Louise, who agrees to store it because her neighborhood covers M.
+:::
+::: storystep
![](/assets/img/concepts/4.10-gossip-resilience.png){.sz80p} {.center}
+---
+
Louise shares it with her neighbor, Norman, in case she goes offline.
+:::
+::: storystep
![](/assets/img/concepts/4.11-retrieval.png){.sz80p} {.center}
-Rosie is a word collector who learns that an interesting new word lives at address is M. She asks her neighbor, Norman, if he has it. Louise has already given him a copy, so he delivers it to Rosie.
+---
+Rosie is a word collector who learns that an interesting new word lives at address is M. She asks her neighbor, Norman, if he has it. Louise has already given him a copy, so he delivers it to Rosie.
:::
## Resilience and availability
@@ -103,24 +123,36 @@ Using information about their neighbors' uptime, cooperating agents work hard to
Let's see how this plays out in the real world.
-::: coreconcepts-storysequence
-
+::: storystep
![](/assets/img/concepts/4.12-healthy-network.png){.sz80p} {.center}
+---
+
An island is connected to the mainland by a radio link. They communicate with each other using a Holochain app.
+:::
+::: storystep
![](/assets/img/concepts/4.13-partition.png){.sz80p} {.center}
+---
+
A hurricane blows through and wipes out both radio towers. The islanders can't talk to the mainlanders, and vice versa, so some DHT neighbors are unreachable. But everyone can still talk to their physical neighbors. None of the data is lost, but not all of it is available to each side.
+:::
+::: storystep
![](/assets/img/concepts/4.14-resilience-building.png){.sz80p} {.center}
+---
+
On both sides, all agents attempt to improve resilience by enlarging their arcs. Meanwhile, they operate as usual, talking with one another and creating new data.
+:::
+::: storystep
![](/assets/img/concepts/4.15-partition-healing.png){.sz80p} {.center}
-The radio towers are rebuilt, the network partition heals, and new data syncs up across the DHT. At this point everyone has the option of shrinking their arc sizes and prune overly redundant data (although experience has shown them that it might be best to overcompensate in case another hurricane comes around).
+---
+The radio towers are rebuilt, the network partition heals, and new data syncs up across the DHT. At this point everyone has the option of shrinking their arc sizes and prune overly redundant data (although experience has shown them that it might be best to overcompensate in case another hurricane comes around).
:::
## A cloud of witnesses
diff --git a/src/pages/concepts/5_links_anchors.md b/src/pages/concepts/5_links_anchors.md
index 1312d29e9..d56613167 100644
--- a/src/pages/concepts/5_links_anchors.md
+++ b/src/pages/concepts/5_links_anchors.md
@@ -2,11 +2,11 @@
title: "Links and Anchors: Connecting DHT Data Together"
---
-::: coreconcepts-intro
+::: intro
Data on the DHT is connected via one-way **links**. They allow you to create a graph database, making information easy to discover. **Anchors** serve as starting points for link discovery.
:::
-::: coreconcepts-orientation
+::: orientation
###
What you'll learn
1. [Why it's hard to find data in a DHT](#the-difficulty-of-looking-for-data-especially-when-you-don-t-know-what-you-re-looking-for)
@@ -67,38 +67,67 @@ Neither the base nor the target of a link need to have any data stored at their
Take note of the arrowheads below; you'll see that many are bidirectional. In Holochain, however, a link is unidirectional. This means that, for a bidirectional link, two links must be created in opposite directions to each other.
!!!
-::: coreconcepts-storysequence
-
+::: storystep
![](/assets/img/concepts/5.2-alice.png){.sz80p} {.center}
+---
+
Alice is a singer/songwriter who excels at the ukulele and wants to share her music with the world. She joins the app and chooses to register the username "@alice_ukulele". She checks if it's already been taken by calculating its address and looking for an existing username DHT entry with that address.
+:::
+::: storystep
![](/assets/img/concepts/5.3-alice-username.png){.sz80p} {.center}
+---
+
That entry doesn't exist, so she creates it and links it to her agent address. Now, users who know her username can find her agent address.
+:::
+::: storystep
![](/assets/img/concepts/5.4-usernames-anchor.png){.sz80p} {.center}
+---
+
Alice wants to show up in the public directory of artists, so she links her username entry to the "_all_users_" anchor, a string constant that's hard-coded into the app. Now the app can discover her username, along with all others, by retrieving all the links on "_all_users_".
+:::
+::: storystep
![](/assets/img/concepts/5.5-alice-album.png){.sz80p} {.center}
+---
+
Alice creates an entry for her debut EP album and links it to her agent address. Now listeners who discover her agent address can find the albums she's published.
+:::
+::: storystep
![](/assets/img/concepts/5.6-album-tracks.png){.sz80p} {.center}
+---
+
She uploads all the tracks and links them to the album entry.
+:::
+::: storystep
![](/assets/img/concepts/5.7-album-genres.png){.sz80p} {.center}
+---
+
Now she wants people to be able to find her album by genre, so she selects or creates a few applicable genre tags (they're anchors too) and links her album to them.
+:::
+::: storystep
![](/assets/img/concepts/5.8-genres-anchor.png){.sz80p} {.center}
+---
+
Those genre tags are linked to an "_all_genres_" anchor, another hard-coded constant. Listeners can query this anchor to get the full list of genres.
+:::
+::: storystep
![](/assets/img/concepts/5.9-graph-database.png){.sz80p} {.center}
+---
+
Alice's entries, now linked to one another and other existing entries on the DHT, form a graph that allows listeners to discover her and her music from a number of different starting points.
:::
diff --git a/src/pages/concepts/6_crud_actions.md b/src/pages/concepts/6_crud_actions.md
index a38ae2546..01ca4170f 100644
--- a/src/pages/concepts/6_crud_actions.md
+++ b/src/pages/concepts/6_crud_actions.md
@@ -2,10 +2,11 @@
title: "CRUD Actions: Modifying and Deleting Data"
---
-::: coreconcepts-intro
+::: intro
Holochain allows agents to 'mutate' immutable data by publishing special **delete** and **update** actions to the DHT.
+:::
-::: coreconcepts-orientation
+::: orientation
###
What you'll learn
1. [Why you can't delete or modify DHT data](#public-immutable-databases)
diff --git a/src/pages/concepts/7_validation.md b/src/pages/concepts/7_validation.md
index e9f8c0f87..965546b3b 100644
--- a/src/pages/concepts/7_validation.md
+++ b/src/pages/concepts/7_validation.md
@@ -2,11 +2,11 @@
title: "Validation: Assuring Data Integrity"
---
-::: coreconcepts-intro
+::: intro
Holochain DNAs can specify **validation rules** for DHT operations. This empowers agents to check the integrity of the data they see. When called upon to validate data, it allows them to identify corrupt peers, author a **warrant** against them as proof of their actions, and take personal defensive action against them.
:::
-::: coreconcepts-orientation
+::: orientation
###
What you'll learn
1. [Why validation matters](#validation-the-beating-heart-of-holochain)
@@ -82,41 +82,69 @@ When you **commit a record**, your conductor is responsible for making sure you'
#### Valid entry
-::: coreconcepts-storysequence
+::: storystep
![](/assets/img/concepts/7.2-commit.png){.sz80p} {.center}
+---
+
Alice calls the `publish_word` zome function with the string `"eggplant"`. The function commits that word to her source chain. The conductor 'stages' the commit in the function's scratch space and returns the creation action's record hash to the `publish_word` function. The function continues executing and passes a return value back to the conductor, which holds onto it for now.
+:::
+::: storystep
![](/assets/img/concepts/7.3-validate.png){.sz80p} {.center}
+---
+
After the function has finished, Alice's conductor [converts this record into DHT operations](../4_dht/#a-cloud-of-witnesses), looks up the integrity zome that defines the `word` entry type, and calls that zome's validation function on each of the operations.
+:::
+::: storystep
![](/assets/img/concepts/7.4-validation-success.png){.sz80p} {.center}
+---
+
The validation function simply checks that the entry data contained in the action is only one word long, returning `Valid`.
+:::
+::: storystep
![](/assets/img/concepts/7.5-persist-and-publish.png){.sz80p} {.center}
+---
+
Her conductor commits the entry to her source chain, clears out the scratch space, and passes the `publish_word` function's return value back to the client. The operations are then sent to the appropriate DHT authorities for validation and integration into their shards.
:::
#### Invalid entry
-::: coreconcepts-storysequence
+::: storystep
![](/assets/img/concepts/7.6-commit.png){.sz80p} {.center}
+---
+
Alice calls the same zome function with the string `"orca whales"`. Again, the function calls `create_entry` and the commit is staged to the scratch space.
+:::
+::: storystep
![](/assets/img/concepts/7.7-validate.png){.sz80p} {.center}
+---
+
Again, the conductor converts the committed action into operations calls the validation function on each of them.
+:::
+::: storystep
![](/assets/img/concepts/7.8-validation-failure.png){.sz80p} {.center}
+---
+
This time, the validation function sees two words. It returns `Invalid("too many words")`.
+:::
+::: storystep
![](/assets/img/concepts/7.9-return-error.png){.sz80p} {.center}
+---
+
Instead of committing the entry, the conductor passes this error message back to the client instead of whatever the `publish_word` function's return value was.
:::
@@ -130,21 +158,35 @@ Here are the two scenarios above from the perspective of a DHT authority.
#### Valid entry
-::: coreconcepts-storysequence
+::: storystep
![](/assets/img/concepts/7.10-gossip-to-authorities.png){.sz80p} {.center}
+---
+
As authorities for the address `E`, Diana and Fred receive a copy of a store-entry operation that stores the `"eggplant"` entry at that address.
+:::
+::: storystep
![](/assets/img/concepts/7.11-authorities-validate.png){.sz80p} {.center}
+---
+
Their conductors call the appropriate validation function.
+:::
+::: storystep
![](/assets/img/concepts/7.12-hold.png){.sz80p} {.center}
+---
+
The operation is valid, so they store the entry and action in their personal DHT stores, along with their **validation receipts** attesting its validity.
+:::
+::: storystep
![](/assets/img/concepts/7.13-respond-validation-receipts.png){.sz80p} {.center}
+---
+
They both send a copy of their receipts back to Alice. Later on, they share the operation with their neighbors for resilience.
:::
@@ -156,33 +198,50 @@ You may remember from our [exploration of the DHT](../4_dht/) that the 'store en
Let's say Alice has taken off her guard rails --- she's hacked her Holochain software to bypass the validation rules.
-::: coreconcepts-storysequence
+::: storystep
![](/assets/img/concepts/7.14-gossip-to-authorities.png){.sz80p} {.center}
+---
+
Norman and Rosie receive a copy of Alice's 'store entry' operation for `"orca whales"`.
+:::
+::: storystep
![](/assets/img/concepts/7.15-validate.png){.sz80p} {.center}
+---
+
Their conductors call the validation function.
+:::
+::: storystep
![](/assets/img/concepts/7.16-warrant.png){.sz80p} {.center}
+---
+
The operation is invalid. They create, sign, and store a **warrant** (a claim that the operation is invalid).
+:::
+::: storystep
![](/assets/img/concepts/7.17-gossip-warrant.png){.sz80p} {.center}
+---
+
Norman and Rosie add Alice to their permanent block lists and remove her data from their DHT shards. When anyone asks for the data at the entry's address, they return the warrant instead.
+:::
+::: storystep
![](/assets/img/concepts/7.18-ejection.png){.sz80p} {.center}
+---
+
Eventually, everyone knows that Alice is a 'bad actor' who has hacked her app. They all ignore her whenever she tries to talk to them, which effectively ejects her from the DHT.
+:::
!!! info What happens when an agent receives a warrant instead of data?
Currently only validation authorities permanently block authors for invalid data; a future release of Holochain will also allow non-authorities to store a warrant they've received and use it as justification for taking personal defensive action against the warranted agent. This will likely look like challenging the warranted agent to produce the potentially invalid data on first contact, then blocking them if the data is indeed valid or warranting the authority if the data is valid and the warrant is erroneous.
!!!
-:::
-
## Use cases for validation
The purpose of validation is to **empower a group of individuals to hold one another accountable to a shared set of rules**. This is a pretty abstract claim, so let's break it down into a few categories. With validation rules, you can define things like:
diff --git a/src/pages/concepts/8_calls_capabilities.md b/src/pages/concepts/8_calls_capabilities.md
index e87086f41..da567d465 100644
--- a/src/pages/concepts/8_calls_capabilities.md
+++ b/src/pages/concepts/8_calls_capabilities.md
@@ -2,11 +2,11 @@
title: "Calls and Capabilities: Communicate With Other Components And Peers"
---
-::: coreconcepts-intro
+::: intro
Application components can **call a cell's functions**. On one agent's device, clients can call functions in cells, and cells in the same conductor can call each other's functions. Within one DHT, cells can call other agents' cells, allowing agents to delegate their agency to others.
:::
-::: coreconcepts-orientation
+::: orientation
###
What you'll learn
1. [Who can call whose functions](#client-inter-zome-bridge-and-remote-calls-who-can-call-whom)
diff --git a/src/pages/concepts/9_signals.md b/src/pages/concepts/9_signals.md
index 5d61236b0..6c42560c3 100644
--- a/src/pages/concepts/9_signals.md
+++ b/src/pages/concepts/9_signals.md
@@ -2,11 +2,11 @@
title: "Signals: Communicating without waiting for a response"
---
-::: coreconcepts-intro
+::: intro
A DNA usually only receives function calls from the outside world and returns a response. But a DNA can also push **signals** to a listening client on the agent's device, or another agent on the same network.
:::
-::: coreconcepts-orientation
+::: orientation
###
What you'll learn
1. [When signals are useful](#when-are-signals-useful)
diff --git a/src/pages/ds/containers.md b/src/pages/ds/containers.md
index 8eb8c68ac..989d66ee3 100644
--- a/src/pages/ds/containers.md
+++ b/src/pages/ds/containers.md
@@ -79,18 +79,18 @@ Can wrap around code fences to prevent Copy buttons form being added to the code
## Site content specific containers
All the below just apply a specific class to a surrounding div. These were implemented for very specific site style needs. More such can be created if need be.
-- ### coreconcepts-intro
- ::: coreconcepts-intro
+- ### intro
+ ::: intro
Sit amet tellus cras adipiscing enim eu turpis egestas. Pretium aenean pharetra magna ac placerat vestibulum lectus mauris ultrices eros in cursus turpis massa tincidunt dui ut ornare lectus sit.
:::
-- ### coreconcepts-orientation
- ::: coreconcepts-orientation
+- ### orientation
+ ::: orientation
Sit amet tellus cras adipiscing enim eu turpis egestas. Pretium aenean pharetra magna ac placerat vestibulum lectus mauris ultrices eros in cursus turpis massa tincidunt dui ut ornare lectus sit.
:::
-- ### coreconcepts-storysequence
- ::: coreconcepts-storysequence
+- ### storystep
+ ::: storystep
Sit amet tellus cras adipiscing enim eu turpis egestas. Pretium aenean pharetra magna ac placerat vestibulum lectus mauris ultrices eros in cursus turpis massa tincidunt dui ut ornare lectus sit.
:::
diff --git a/src/pages/get-started/install-advanced.md b/src/pages/get-started/install-advanced.md
index 7e2b5ad44..41336639b 100644
--- a/src/pages/get-started/install-advanced.md
+++ b/src/pages/get-started/install-advanced.md
@@ -4,7 +4,7 @@ hide:
- toc
---
-::: coreconcepts-intro
+::: intro
This guide assumes that you've already followed the [quick installation guide](/get-started/) and want to learn more about the set up. It describes how to manually recreate and maintain the development environment, use your default shell and preferred code editor with Nix, explains how to install specific versions of Holochain, and discusses why we use `nix develop` in the first place.
:::
diff --git a/src/pages/get-started/install-without-nix.md b/src/pages/get-started/install-without-nix.md
index 37ee3ef38..6997565dc 100644
--- a/src/pages/get-started/install-without-nix.md
+++ b/src/pages/get-started/install-without-nix.md
@@ -4,7 +4,7 @@ hide:
- toc
---
-::: coreconcepts-intro
+::: intro
If you ended up here because you ran into problems with the [Nix based quick installation guide](/get-started/), we would greatly appreciate if you let us know what went wrong by [creating a bug report](https://github.com/holochain/docs-pages/issues/new/choose) so that we can look into it.
:::
diff --git a/src/pages/get-started/upgrade-holochain.md b/src/pages/get-started/upgrade-holochain.md
index a531ebe12..6851f1c0f 100644
--- a/src/pages/get-started/upgrade-holochain.md
+++ b/src/pages/get-started/upgrade-holochain.md
@@ -4,7 +4,7 @@ hide:
- toc
---
-::: coreconcepts-intro
+::: intro
For existing hApps that have been written for Holochain version 0.1, these are steps to upgrade to Holochain version 0.2.
:::
@@ -72,8 +72,8 @@ whenever you update your Cargo dependencies to the latest HDI/HDK versions.
Check if your `flake.nix` still contains the Node.js package that was included by the scaffolding. It will look something like
```nix
-packages = [
- pkgs.nodejs-18_x
+packages = [
+ pkgs.nodejs-18_x
];
```
diff --git a/src/scss/_debug.scss b/src/scss/_debug.scss
index 5e8281351..4e70bae41 100644
--- a/src/scss/_debug.scss
+++ b/src/scss/_debug.scss
@@ -1,8 +1,8 @@
-.h-tile-container, .home-tiles,
+.h-tile-container, .home-tiles,
// .h-tile, .tile-hero,
-.coreconcepts-intro,
-.coreconcepts-orientation,
+.intro,
+.orientation,
.h-tile-container-3, .tile-tabs,
.tabcontent, .content_linux, .content_macos, .content_windows,
.h-author {
diff --git a/src/scss/_fonts.scss b/src/scss/_fonts.scss
index 986c214cd..b05d8c2bf 100644
--- a/src/scss/_fonts.scss
+++ b/src/scss/_fonts.scss
@@ -5,28 +5,28 @@
font-family: "URW Form";
font-weight: 700;
font-style: normal;
- src: url('/assets/webFonts/URWFormBold/font.woff2') format('woff2'),
+ src: url('/assets/webFonts/URWFormBold/font.woff2') format('woff2'),
url('/assets/webFonts/URWFormBold/font.woff') format('woff');
}
@font-face {
font-family: "URW Form";
font-weight: 700;
font-style: italic;
- src: url('/assets/webFonts/URWFormBoldItalic/font.woff2') format('woff2'),
+ src: url('/assets/webFonts/URWFormBoldItalic/font.woff2') format('woff2'),
url('/assets/webFonts/URWFormBoldItalic/font.woff') format('woff');
}
@font-face {
font-family: "URW Form";
font-weight: 800;
font-style: normal;
- src: url('/assets/webFonts/URWFormExtraBold/font.woff2') format('woff2'),
+ src: url('/assets/webFonts/URWFormExtraBold/font.woff2') format('woff2'),
url('/assets/webFonts/URWFormExtraBold/font.woff') format('woff');
}
@font-face {
font-family: "URW Form";
font-weight: 800;
font-style: italic;
- src: url('/assets/webFonts/URWFormExtraBoldItalic/font.woff2') format('woff2'),
+ src: url('/assets/webFonts/URWFormExtraBoldItalic/font.woff2') format('woff2'),
url('/assets/webFonts/URWFormExtraBoldItalic/font.woff') format('woff');
}
@@ -38,21 +38,22 @@ $fs-extra-smaller: 13px;
$fs-smaller: 14px;
$fs-primary: 18px;
$fs-bigger: 20px;
+$fs-even-bigger: 24px;
// local font sizes
$fs-footer-col-header: 14px;
$fs-footer-smaller: $fs-smaller;
html {
- font-size: 6.2%;
-
- body {
+ font-size: 62.5%;
+
+ body {
font-family: Mulish;
font-size: $fs-primary;
}
-
- h1, h2, h3, h4 {
- font-family: 'URW Form';
- font-weight: 800;
+
+ h1, h2, h3, h4 {
+ font-family: 'URW Form';
+ font-weight: 800;
}
}
diff --git a/src/scss/_values.scss b/src/scss/_values.scss
index 4f8c36514..21377ae32 100644
--- a/src/scss/_values.scss
+++ b/src/scss/_values.scss
@@ -2,6 +2,7 @@
$cl-brand-purple: #8719F6;
$cl-brand-green: #0EDDD3;
$cl-other-green: #74FAD2;
+$cl-lightest-gray: #f7f7f7;
$cl-gray: #d0d0d7;
$cl-light-gray: #575656;
@@ -11,7 +12,7 @@ $cl-footer-bg: black;
// Font sizes defined in _fonts
-// Spacing
+// Spacing
$sp-icon-list-gap: 16px;
/* Breakpoints & Widths */
@@ -28,7 +29,7 @@ $footer-large-screen-height: 200px;
/* Gradients */
-$header-gradiant: linear-gradient(90deg,#4720e3,#345ede 32.81%,#0dddd3);
+$header-gradiant: linear-gradient(90deg,#4720e3,#345ede 32.81%,#0dddd3);
$blue-to-light-purple-grad: linear-gradient(180deg,#4720e3,#b748ff 68.44%);
$blue-to-light-blue-grad: linear-gradient(180deg,#415bed,#13d0d7 44.86%);
diff --git a/src/scss/_widgets.scss b/src/scss/_widgets.scss
index 7bbce91ec..49a6be31b 100644
--- a/src/scss/_widgets.scss
+++ b/src/scss/_widgets.scss
@@ -201,7 +201,7 @@ details {
}
&.learn {
- background-color: #f7f7f7;
+ background-color: $cl-lightest-gray;
border-radius: 5px;
outline: none;
@@ -375,4 +375,36 @@ a.link-tile {
width: 80vw;
height: 80vh;
}
+}
+
+.intro {
+ font-size: $fs-even-bigger;
+ border-left: 4px solid $cl-gray;
+ padding-left: 1em;
+ font-weight: 500;
+ line-height: 1.55;
+}
+
+.orientation, .storystep {
+ padding: 1.5em;
+ margin: 1.5em 0;
+ background: $cl-lightest-gray;
+
+ > :first-child {
+ margin-top: 0;
+ }
+
+ > :last-child {
+ margin-bottom: 0;
+ }
+}
+
+.storystep {
+
+ /* Horizontal rules can be used to break up content in a story step.
+ Make them nice and light. */
+ hr {
+ border: 1px solid $cl-gray;
+ margin: 1.5em -1.5em;
+ }
}
\ No newline at end of file
diff --git a/src/scss/page-specific/_default-page-type.scss b/src/scss/page-specific/_default-page-type.scss
index 3cdc7997a..659d90e0f 100644
--- a/src/scss/page-specific/_default-page-type.scss
+++ b/src/scss/page-specific/_default-page-type.scss
@@ -1,4 +1,4 @@
-$cl-sidebar-bg: #f7f7f7;
+$cl-sidebar-bg: $cl-lightest-gray;
&.default-page-type {
background-color: white;
diff --git a/src/scss/site.scss b/src/scss/site.scss
index 82b5ee55f..795f68796 100644
--- a/src/scss/site.scss
+++ b/src/scss/site.scss
@@ -28,7 +28,7 @@ body {
// two section content, right side bar, main chunk on left.
@import 'page-specific/default-page-type';
@import 'page-specific/ds-layout-type';
-
+
// }
@import 'footer';