Welcome

Status

This documentation is a work in progress. Articles which have content in them appear normally in the chapter navigation. Articles which are incomplete appear with a (E) next to their name, indicating it is an empty unwritten article.

Hi there! You've discovered the comprehensive Holochain guidebook.

Holochain is an open source software library that provides a way for businesses, communities, and other groups to build and run applications which are hosted and validated by the "users" themselves. Doing so provides a superior level of agency and autonomy over heavy reliance on the so-called "cloud" and other third parties.

These applications are known more widely as peer-to-peer, decentralized applications, or dApps. To distinguish between dApps built on Blockchains and those built on Holochain, we preemptively call the latter "hApps". A more detailed comparison between Blockchain dApps and Holochain hApps is available here.

Holochain provides a cross-platform framework for the development and execution of these applications. By running these kinds of applications, "users" cease to merely "use". They become "user-participants" who are also responsible for hosting and validating the network's data. Applications can be developed utilizing any of the major operating systems, and run on virtually any device.

The many benefits and opportunities associated with peer-to-peer dApps (e.g. offloaded server costs, elimination of single points of failure, and flexible governance structures) are made available, and often amplified through the Holochain hApp architecture, on desktops, laptops, and Android (arm64) devices.

This book provides an in-depth explanation of Holochain's functions, from data validation to data propagation, so that you can get to work straightaway on developing applications that will serve your business, community, or otherwise.

Overview

Depending on whether your interest is specific, or general, you may wish to read it front to back, or skip to specific sections of the book. If the summary of a section describes you, then it's a section for you!

Planning a dApp

Readers: You can't put the horse before the cart. First things first: understanding the landscape of decentralized apps. What are the mechanics of a functioning decentralized app? You have an idea of what you want to build, and you need to get a grasp on what it's going to take. You need to know what your blind spots are, and what are the common pitfalls. You need to know which of your assumptions to hold on to, and which to let go.

Writing: General Audience

Building Holochain Apps

Readers: Whether you're a seasoned developer, or just starting out, you're in it to write code. You've either got an app project (or five) on the go, or you're in it to experiment and test the limits. You need to know what you need to know. You want to talk Holochain's language. You're an intrepid explorer of technical documentation.

Writing: Technical, Explanatory & How-to

Going Live with Holochain Apps

Readers: You're involved in the conception, development, or design of a Holochain app, and you've got to know how to get your app out into the world, into the hands of the people who need it. You've got questions like "How do updates to the app work?", "How do I track performance of the app?", "What are best practices for security?"

Writing: General Audience

Extending the Holochain Platform

Readers: You want to look at Holochain itself, not what you can build with it, but to see what you can tweak, or contribute. You've got ideas for Holochain and got skills to pull them off. You're reading the Holochain source code, or source documentation. Maybe you want to enable app development in a whole other language not available yet, or maybe to run Holochain on a device or platform not supported yet. You can make sense of terse technical language, and direct yourself well.

Writing: Technical, Explanatory & How-to

How to contribute

This book uses a tool that builds HTML files from markdown files called 'mdbook'. The markdown files are stored on GitHub, in the main holochain-rust repository. Because they are on GitHub, they have built-in version control, meaning that it's easy for anyone to contribute, and to propose, track and merge changes.

There are two main pathways to contribute. One is by editing files directly in GitHub, and the other is cloning the repository to your computer, running mdbook locally, and then submitting change requests. The following covers both scenarios.

Writing Guidelines

Please do not edit the SUMMARY.md file, which stores the raw chapter structure, without advanced signoff from the documentation team in https://chat.holochain.net/appsup/channels/hc-core.

More forthcoming!

How the Book Works

Writing is in classic markdown, so a new page is a new markdown file. These files get rendered in the book panel. One markdown file, SUMMARY.md stores the structure of the book as an outline.

The HTML files used in the Book get automatically built from the markdown files. Even though they're auto-generated, static HTML files, one can search within the text.

What to Contribute

For a current list of open documentation issues, check out the 'documentation' label for github issues.

Contributing via GitHub

Getting there

  1. Log on to your GitHub account

  2. In the Holochain Rust repo, everything is under the doc/holochain_101/src folder. All markdown files are there, and some are nested in subfolders. Navigate to the following link: https://github.com/holochain/holochain-rust/tree/develop/doc/holochain_101/src

  3. Determine whether you are making, editing, or reviewing an article.

Access Rights

If you don't have write access to the repository you need to create a fork to contribute. Forking is easy. Click the "Fork" button in the top right hand corner of the Github UI.

Making a new article

  1. Click "Create New File"

  2. Name this file what you intend to name the article, plus the .md extension, i.e. how_to_contribute.md

  3. Use classic markdown to set up the page title, i.e. "# How to contribute"

  4. Write the rest of your text, checking the "Preview" tab to see how it would look.

  5. Scroll to the bottom of the page and select the option "create a new branch for this commit and start a pull request". You can name a branch, though GitHub will set one automatically. If you know it, mention the issue that the request addresses.

  6. Click "Propose New File". Proceed to the Making Multiple Edits or Opening a Pull Request section.

Editing an article

  1. Navigate to the article you want to edit.

  2. Click the 'pencil' icon to edit the article. There's a built-in text editor in GitHub, where you can write a change and also why you changed it (so that a reviewer can understand the rationale for the change).

  3. Select the branching method for making your change. (See Making Multiple Edits for clarification)

  4. Click "Propose File Change". Proceed to the Making Multiple Edits or Opening a Pull Request section.

Making Multiple Edits On One Branch & Pull Request

A "branch" is a series of divergent changes from the main version. If you want to make multiple edits at once, you will need to make each of those changes on the same branch as you named your original edit. Check which branch you are on by looking for the "Branch: ?" dropdown. Use the dropdown to switch to your branch if you're on the wrong one.

Opening a Pull Request

  1. Once redirected to the "comparing changes" page, prepend your pull request title with "MDBOOK: " and then a very short statement of what changed.

  2. Add a more detailed description of what changes you made and why in the text box.

  3. If there is an open issue related to the article you're submiting or editing, tag it by using the "#" plus the issue number.

  4. Add the "documentation" label.

  5. If appropriate, click "Reviewers" and select one or more people to request reviews from.

  6. Click "Create Pull Request".

Reviewing a Pull Request

  1. Under the Pull Request tab, look for ones starting with "MDBOOK". Go to the Pull Request of your choice, and then click on the "Files Changed" tab.

  2. Start a review by hovering over a line and pressing the blue "add" symbol to add comments to a line

  3. Click the green "Review Changes" button. If you approve of the changes, select "Approve". If you would like further changes to be made before it gets merged, select "Request Changes". If you are just weighing in, select "Comment". Then, click "Submit Review".

Merging a Pull Request

  1. Under "Conversation" you can merge the pull request, which integrates it into the develop branch. Changes automatically deploy to https://holochain.github.io/holochain-rust within ~30 minutes. Merge the pull request once it has received two approved reviews.

Contributing by Cloning and Running Mdbook (advanced)

You will need to have cloned holochain-rust to your computer. You will also need Docker installed.

There is a Docker build that allows local build, serve, watch and live reload for the book.

From the root of the repo, run:

. docker/build-mdbook-image && . docker/run-mdbook

Once the book has built and is serving, visit http://localhost:3000 in the browser.

You can edit the markdown files in doc/holochain_101/src and the book will live reload.

To do a one-time build of the files to HTML, run:

. docker/build-mdbook

Edit the files to your satisfaction, commit them to a branch (or a fork) and then open a pull request on GitHub. Once its on GitHub, the same things as mentioned above apply.

Planning a dApp

What is a dApp?

A dApp is a distributed application. This means that the data associated with the application is stored by each user rather than in a central database.

Basic expectations for dApps

Generally speaking, you need to know the following in order to build a Holochain dApp:

how to install Holochain

how to use the command line tools

how to configure your application with a "DNA" file

how to write your application code in a language that compiles to WebAssembly

how to think through building a distributed application

how to build a user interface for your app

how to test your application code

This article will help you plan a dApp by providing practical considerations about the specifics of distributed applications in general, and Holochain dApps in particular. It has been remarked that holochain dApps require us to make a mental shift, first from applications whose data is centrally organized, and also from blockchain-based, data-centric dApps.

Here we will provide a basic overview of concepts from cryptography that are central to holochains. Then, we will consider the consequences of Holochain's cryptographic architecture for data permissioning, access, and security. Because app data storage is distributed amongst user-participants, one must expect that data encryption and permissions are important for protecting privacy in accordance with the jurisdictions in which the app is operating.

Remember that, as user-participants leave the application, they take their data with them. They also retain copies of other data that they held to support the DHT.

One must also re-think the dApp's business model such that it does not rely on a central authority's ability to whitelist access to a given resource.

Cryptography in Holochain dApps

Distributed systems rely more heavily on cryptographic patterns and techniques than centralized systems. The basic concepts below explain how data integrity, ownership, and security are achieved natively with holochain's architecture. They are time-worn, relatively intuitive ideas that are critical for planning a holochain dApp.

Hashes

Hashes ensure the reliability of information by representing any given piece of data with a unique, consistent string of random looking characters. This makes changes to data visible because one can see that a hash has changed hash without needing to inspect the data itself.

However, it is impossible to get the original data from a hash -- its purpose is to prove that the data to which it corresponds has not been altered. The same data consistently gives the same hash, and different data always gives a completely different hash.

These features imply that one can use small, portable hashes to verify data. One could also use a database containing data and their hashes as a table of contents, indexing (though not reading) data associated with a given hash.

Signatures

Signatures provide an additional type of data verification, answering the question "who created this data?" Signatures looks like hashes. They are unique, reliable, and like hashes, cannot be used to retrieve the data to which they correspond. Signatures also come with a pair of keys. One is public, and the other private.

The private key designates a unique author (or device), and the public key lets anyone verify a signature made by one specific private key. This key infrastructure addresses the problem of single points of failure associated with centralized systems by making each author responsible for securing their unique private key.

Encryption

What if one needs to restrict access in addition to verifying data? Two types of encryption are possible. Symmetric encryption has one key for reading and writing data. Asymmetric encryption has two keys, where one creates messages and the other reads them.

Encryption is a two way process, so the right key enables one to decrypt an encrypted message. With this added benefit come the drawbacks of the size of encrypted messages (at least as large as the original data) and broken encryption stripping the author of control of the original data.

Data access paradigms

The following are five data access paradigms. Note that in real-world scenarios it is common to mix these options by combining separate dApps. In instances when many separate dApps are needed to share data, Holochain supports bridging between dApps Bridges between two networks with different data privacy models specify who can use the bridge, what data crosses the bridge, and tasks that might run in response to the bridge (e.g. notifications)

Default model for Holochain data is public data shard on a public network, and every Holochain dApp has its own network and data, and creates networks for user-participants as soon as they join a dApp. The dApp code sets sharing and verification rules.

Public, shared data on a public network

Public data works like Bittorrent:

Anybody can join a network anybody can request any data they want from the network any data is available as long as at least one person is sharing it if some data is not shared by enough people, a new random person on the network must share it there is no "local only" data

As stated above, an additional requirement for Holochain dApps is that new data must have a digital signature.

Public, shared data on a private network

The functionality is the same as a public network, but private networks use cryptography for access control to the network itself.

Each device on the network must open a P2P connection to another device before it can send any data. The devices that are already on the private network send a challenge to any new device before sending any more data. The new device must sign the challenge with the network private key. The network public key is set in the dApp configuration, available to Holochain. Holochain can then refuse any connection with a bad challenge signature. Data within the network is public and shared. Every device on the network has “logged in” with a signed challenge, so has full access.

Encrypted data on a public or private network

Encryption relies on dApp developers encrypting and decrypting data within the dApp software.

Holochain exposes a set of industry standard encryption algorithms (e.g. AES) to each dApp that cover both symmetric and asymmetric encryption options, in addition to hashing and signing tools.

This option is very flexible for dApp developers but security becomes more subtle. Much like the private network, any one user-participant losing a key can become a serious security problem.

Note that encryption can pose problems for Holochain's native validation method.

Private, local only data

Any data in a dApp can be validated and stored by its author without being shared to the network. Private, local data can provide a useful database for portable user preferences and notes and avoids the complexity of encryption and key-based security.

Private data is hashed in the same way as public data, and the hash is public. Accordingly, one could tell that private data exists without being able to access it or take advantage of this with dApps that feature the eventual reveal of previously authored, private data -- think a distributed guessing game, like "rock, paper, scissors" or a digital classroom that operates with signatures disconnected from real-world identity and uses this method to prevent cheating.

Hybrid model with servers

Holochain supports many different environments and interfaces so that Holochain is easy to integrate with existing infrastructure. Any connected system with an API can push data through a dApp, as when one's phone sends a summary of private calendar data to a Holochain dApp. Any data in a dApp immediately becomes transparent, auditable and portable.

The version of Holochain in active development covers the following integrations:

  • Command line tools
  • Web server
  • Android
  • Qt/QML
  • Unity 3D games engine

Security - best practices

A great way to begin offsetting the governance crises now typical of distributed systems (i.e. DAO hack) is to think in terms of protecting and enabling the community of user-participants in addition to cryptography.

In essence, one must consider how to prevent undesired access to the DHT. If membranes are not properly built in the dApps's DNA, having access to the source code also means having access to the entire network's entries via the DHT. Developers must treat the code, or at least the DNA taken as a whole, as if it's a key to the data. Note, too, that one can easily fork a Holochain dApp without disrupting its activity, making it possible to retain the benefits of open-source code without some of the risks.

Membranes

Security efforts begin with the specification of membranes, lest the code itself become a target. Though holochains rely on the cryptography above to create trust in data's provenance and immutability, trust is a distinctly human affair at the level of creating membranes. Different applications will require different levels of security, and Holochain is uniquely suited to accommodate a high degree of flexibility. DNA could define a closed list of participants, Proof of Service, or social triangulation requirements, for example.

Sybil attacks are attacks launched through the creation of many nodes explicitly for the purpose of the attack. These nodes are identifiable by having no history. Blockchains prevent Sybil Attacks with Proof of Work. PoW is an implied membrane since it constrains who can verify blocks. In that case, the clearing node must have done "work" to maintain the network. Holochain requires membranes to identify and filter out Sybil nodes so that an attacker cannot use them to overrun the DHT.

Immune system

Holochain relies on random users across the network validating every piece of data. This keeps the verification rules reliable and unbiased. This is called an "immune system" for validating content.

Note that when using encrypted data, it is not possible to verify contents without the appropriate key. Encryption is a choice that should be made carefully, as it undermines Holochain's native immune system.

Scenarios to consider

  1. p2p platforms
  2. supply chains and open value networks
  3. social networks
  4. collaboration apps

Building Apps

If you're looking to build a Holochain app, it is important to first know what a Holochain app is.

First, recall that Holochain is an engine which can run your distributed apps. That engine expects and requires your application to be in a certain format which is unique to Holochain. That format is referred to as the "DNA" of your application. The DNA of an application exists as a singular file, which is mounted, and executed by Holochain.

However, writing your application in a single file, as a developer, would not be feasible or desirable. Instead, you are supplied the tools to store your application code across a set of files within a folder, and tools to build all that code down into one file, in the DNA format.

While there are lots of details to learn about Holochain and DNA, it can be useful to first look from a general perspective.

Holochain and DNA

Recall that a goal of Holochain is to enable cryptographically secured, tamper-proof peer-to-peer applications. DNA files play a fundamental role in enabling this. Imagine that we think of an application, and its users, as a game. When people play any game, it's important that they play by the same rules, otherwise, they are actually playing different games. With Holochain, a DNA file contains the complete set of rules and logic for an application. Thus, when users independently run an app with identical DNA, they are playing the same game: running the same application with cryptographic security.

What this allows in technical terms is that these independent users begin sharing data with one another, and validating one anothers data. Thus, users can interact with the data in this distributed peer-to-peer system with full confidence in the integrity of that data.

The key takeaway from this is that if you change the DNA (the configuration, validation rules, and application logic), and a user runs it, they are basically running a different app. If this brings up questions for you about updating your application to different versions, good catch. This concern will be addressed later in this section.

Before exploring the details of Holochain DNA, take a minute to explore the different platforms that you can target with Holochain.

Building for Different Platforms

Holochain apps aren't limited to running only on laptop and desktop computers. Because Holochain is written in Rust, Holochain apps will be able to run on platforms ranging from Raspberry Pis to Android smartphones once the tools have been fully developed.

So far, by utilizing bindings for the C language with Holochain, a cross-platform tool for starting and stopping app instances has been developed, called HoloSqape. This particular implementation targets Ubuntu (and Linux), MacOS, and Windows. Note that if you are looking for a good language to develop a cross-platform GUI in, Qt and Qml which is utilized by HoloSqape is a good option.

A tool such as HoloSqape, which can load, start, and stop a Holochain app instance (or apps), is called a "Container" in Holochain terminology.

Another approach to running Holochain apps on different platforms would be to include Holochain itself in your native application, whether it be an Electron app, or an Android app.

There has been some work done to explore building Holochain for Android. If the technical details of this interest you, see this article

Now that you know what's possible in terms of platform options, carry on with getting to know Holochain app development!

Introduction to DNA: Configuration

As a developer, you won't have to interact directly with the contents of a DNA file that often. However, it is quite important to grasp its role and structure.

Holochain DNA files are written in a data format known as JSON. It stores sets of key-value pairs, and allows a nested tree structure. It looks like this:

{
  "property_name": "property_value",
  "nest_name": {
    "nested_property_name": "nested_property_value"
  }
}

JSON is usually used for configuration and static data, but in the case of Holochain, these DNA files also contain compiled code, which is executable by Holochain.

As previously mentioned, you do not need to edit this "master" DNA file directly. Holochain command line tools can be used to build it from your raw files.

Learn more about the package command which fulfills this function

Configuration

For the configuration-related parts of your DNA, they will come from actual JSON files stored in your application folder. There will be multiple JSON files nested in the folder structure. An application folder should have a file in its root called app.json.

This file should define various properties of your application. Some of these properties Holochain fully expects and will not work without, others can be customised to your application.

app.json Properties

A default app.json file looks roughly like this:

{
  "name": "Holochain App Name",
  "description": "A Holochain app",
  "authors": [
    {
      "indentifier": "Author Name <[email protected]>",
      "public_key_source": "",
      "signature": ""
    }
  ],
  "version": "0.0.1",
  "dht": {},
  "properties": null
}

Intro to DNA: Code

The functionality of Holochain applications is written as a collection of logical modules called "Zomes".

Zomes are created inside a folder called zomes, and each Zome should have its own sub-folder within that, in which the configuration and code for that particular Zome should be placed.

These Zomes can call and access the functionality of the others, but they are written independently.

When the DNA file is being packaged, the code for these Zomes is encoded using Base64 encoding and combined with the configuration file associated with the Zome.

The configuration file should be a JSON file, stored in the Zome folder. The file can be named anything, but the default is zome.json.

This zome file is extremely simplistic at this point, and contains only a description property, which is a human readable property that describes what the Zome is for.

The only coding language that Holochain knows how to execute is WebAssembly. However, it is unlikely that you'll want to write WebAssembly code by hand. Instead, most people will write their Zomes' code in a language that can compile to WebAssembly, such as Rust or Assemblyscript, and then define a build step in which it is compiled to WebAssembly. There is already a large, and growing, number of languages that compile to WebAssembly.

If this is sounding complex, don't worry. There are tools supplied to make this easy, and you'll be writing in a language that's familiar, or easy to learn.

With this overview in mind, the details of app development can be explored.

Intro to Command Line Tools

It will likely not come as much of a surprise that to develop a Holochain app requires the use of the command line, or terminal, of your computer.

Fortunately, there are a set of custom designed tools for working with Holochain that can be installed as utilities to your command line to simplify and accelerate the process of Holochain app development.

If you wish to be able to attempt what you are reading about, as you continue through the articles on building an app, you will need to first install these command line tools to your computer.

The command tools are accessible for installation from an online repository at github.com/holochain/holochain-rust/tree/develop/cmd. If you wish to install them, follow the link, and then follow the instructions in the README there for installation.

The README contains an overview of the different functions of the command line tools. You will also learn these organically just by proceeding through the other articles in this section. For example, how to use them to bootstrap a brand new project folder for your app.

Create A New Project

The command line tools discussed in the last article can be used to easily create a new folder on your computer, that contains all the initial folders and files needed for a Holochain application.

You will typically want to create a new project folder for a Holochain application this way. This one approach will suit the creation of a new Holochain app or implementing an existing app with Holochain instead.

In your terminal, change directories to one where you wish to initialize a new Holochain app. The command will create a new folder within the current directory for your app.

Come up with a name for your application, or at least for your project folder.

Copy or type the command below into your terminal, except replace your_app_name with the name you came up with. Press Enter to execute the command.

hc init your_app_name

hc specifies that you wish to use the Holochain command line tools. init specifies to use the command for initializing a new project folder. your_app_name is an argument you supply as the app, and folder name.

This has created a new folder in which you have the beginnings of a Holochain app.

It looks something like this, where the ellipses (...) indicate a folder

  • test
    • ...
  • zomes
    • ...
  • .gitignore
  • .hcignore
  • app.json

test contains some starter code for writing tests.

zomes will contain sub-folders, each of which represents a "Zome", which can be thought of as a submodule of the source code of your DNA.

.gitignore contains useful defaults for ignoring files when using GIT version control.

.hcignore is utilized by the packaging commands of the hc command line tools

app.json is the top level configuration of your DNA.

Carry on to the next article to see about making changes to the configuration of a new project.

Configuring an App

As mentioned in Intro to DNA: Configuration at the top level of a Holochain app source code folder there should be a file named app.json. This file is useful for two primary things:

  1. When executing your application, Holochain can adopt specific behaviours, that can be configured in the app.json file. These mostly relate to how the Distributed Hash Table and P2P gossip functions.
  2. You can give app users, and other developers background info about your application, such as the name of the app, and the author.

Here are the properties currently in use:

Property Description
name Give this application or service a name.
description Describe this application or service for other people to read.
authors Optionally provide contact details for the app developer(s). It is an array, so multiple people can be referenced.
authors.identifier A string including a name, and a public email for the contact person.
authors.public_key_source Can reference a publicly hosted cryptographic "public key" from a private-public key-pair.
authors.signature The app developer can optionally add a string that is signed by their private key, so that app users could verify the authenticity of the application.
version Provides a version number for this application. Version numbers are incredibly important for distributed apps, so use this property wisely.
dht This is a placeholder for the configuration options that Holochain will implement, regarding the Distributed Hash Table. It will provide a number of ways that the DHT behaviour can be customized.
properties Properties, if used, can be an object which implements numerous app specific configuration values. These can be up to the app developer to define, and, when implemented, will be able to be called using the property function of the Zome API.

The minimum recommended values to set when you initialize a new project folder are:

  1. name
  2. description
  3. authors
  4. version

To edit them, just open app.json in a text editor (preferably one with syntax highlighting for JSON), change the values, and save the file.

Writing in Rust

It has always been in the designs for Holochain to support programming in multiple languages. In the prototype of Holochain, Zomes could be written in variants of Javascript (ES5) and Lisp (Zygomys). In the new version of Holochain the primary "Ribosome", where there was a JS one and Lisp one before, interprets WebAssembly code.

While we will provide a small introduction to WebAssembly shortly, we should also briefly introduce Rust, since it is the first language that has a first class Holochain app development experience, with its' WebAssembly compilation. This is accomplished via a Holochain Development Kit (HDK) library which has been written for Rust.

For the time being, writing Holochain apps requires writing code in Rust. This will not always be the case. Assemblyscript, a language based off Typescript, is the next likely language in which there will be an HDK library. If it happens to interest you, there is an article here about writing an HDK, since that is something we also invite and encourage the community to do.

From Wikipedia: "Rust is a systems programming language with a focus on safety, especially safe concurrency, supporting both functional and imperative paradigms."

Rust is a strongly typed language, which is desirable for the development of secure P2P applications, and compilation from Rust to WebAssembly is extremely easy.

If Rust is new to you, don't worry. With lots of Holochain app development happening in an open source way, and through learning resources like this guidebook, and the "Rust book" you will have lots to reference to get started.

While there are lots of other materials available for learning Rust, the base materials for the language are always a good resource to go back to: Rust Docs.

Writing in Assemblyscript

As mentioned in writing in Rust Assemblyscript is a language based off of Typescript, which is designed to compile to WebAssembly. It is hoped that Assemblyscript will soon be mature enough, and have the necessary features, to be able to have an HDK for it. Work on an HDK for Assemblyscript commenced earlier this year, but hit temporary roadblocks.

Updates on this should appear in a number of places:

Intro to WebAssembly

What is WebAssembly exactly?

"WebAssembly is a standard being developed by the W3C group for an efficient, lightweight instruction set. This means we can compile different types of programming languages ranging from C/C++, Go, Rust, and more into a single standard... WebAssembly, or WASM, for short, is memory-safe,platform independent, and maps well to all types of CPU architectures efficiently." - source

Though initially designed for use by major browsers IE, Chrome, Firefox and Safari, WASM has quickly been taken up as a portable target for execution on native platforms as well.

WebAssembly.org describes it as a binary instruction format for a stack-based virtual machine.

Despite being a binary format, "WebAssembly is designed to be pretty-printed in a textual format for debugging, testing, experimenting, optimizing, learning, teaching, and writing programs by hand."

This textual format is called WAT.

Not because it needs to be understood, but so that you can get a glimpse of what WAT looks like, here's a little sample:

(module
    (memory (;0;) 17)
    (func (export "main") (param $p0 i32) (result i32)
        i32.const 6
    )
    (data (i32.const 0)
        "1337.0"
    )
    (export "memory" (memory 0))
)

Once the above code is converted from WAT to binary WASM it is in the format that could be executed by the Holochain WASM interpreter.

Often times, for a language that compiles to WASM, you will have a configuration option to generate the (more) human readable WAT version of the code as well, while compiling it to WASM.

While the compilation to WASM mostly happens in the background for you as an app developer, having a basic understanding of the role of WebAssembly in this technology stack will no doubt help you along the way.

Updating to Alpha 2

If you wrote an application for holochain-proto, you are likely wondering what it may take to port your app to the new holochain-rust version of Holochain.

The following should provide multiple levels of insight into what this could involve.

At a very general level:

  • In terms of code, you have at least 2 options
    • rewriting the code in Rust
    • waiting for Assemblyscript support, and migrating at that point to Assemblyscript (the caveat to this approach is that it is not yet known at which point this support will arrive)
  • The API between a user interface and Holochain has switched from HTTP to Websockets (for now), and so any user interface must be updated to use this approach.
  • The DNA file has been simplified. Less is defined as JSON in the dna.json file and more is defined in the code.
  • Testing of DNA utilizes Nodejs to run tests, using the testing library of your choice. This replaces the custom (and limited) JSON test configuration employed by holochain-proto.
  • Schemas for entry types are no longer defined using json-schema, but using native Rust structs.

At the level of the code, in more detail, the changes are as follows (note that this is in reference to Javascript Zomes being ported to Rust Zomes):

  • all camel case function names are now snake case
  • makeHash is now named entry_address
  • commit is now named commit_entry
  • get is now named get_entry
  • update is now named update_entry
  • remove is now named remove_entry
  • Links are no longer created using commit, but instead have their own method, named link_entries
  • Instead of being implicitly imported, the Zome API functions are explicitly imported into Zomes, e.g. extern crate hdk;
  • The code of each Zome must now utilize a Rust "macro" called "define_zome!", and its various subproperties, which did not previously exist.
  • Many aspects of validation have changed, see the section below on validation

Updating Validation

There is a conceptual change to the approach to validation of entries, and even whereabouts that logic lives in the code.

In holochain-proto, there were a number of hooks which Holochain would call back into, to perform validation, such as

  • validateCommit
  • validatePut
  • validateMod
  • validateDel
  • validateLink

Regardless of how many entry types there were, there would still be only 5 callbacks defined maximum. These validation callbacks were performed at a certain stage in the lifecycle of an entry.

Now, an entry type is defined all in one place, including its validation rules, which are unique to it as an entry type. This could look as follows:


# #![allow(unused_variables)]
#fn main() {
#[derive(Serialize, Deserialize, Debug, DefaultJson)]
struct Person {
    name: String,
}
#}

# #![allow(unused_variables)]
#fn main() {
entry!(
    name: "person",
    description: "",
    sharing: Sharing::Public,
    native_type: Person,
    validation_package: || {
        hdk::ValidationPackageDefinition::Entry
    },
    validation: |person: Person, validation_data: hdk::ValidationData| {
        (person.name.len() >= 2)
            .ok_or_else(|| String::from("Name must be at least 2 characters"))
    }
)
#}

The callback validation, replaces validateCommit and all the rest from holochain-proto. However, validation still happens at various times in the lifecycle of an entry, so if the validation is to operate differently between initial commit to the chain, update, or remove, then that logic must be written into this single validation function. To determine which context validation is being called within, you can check in a property of the second parameter of the callback, which in the example above is called validation_data.

For this, you can use the Rust match operator, and check against the validation_data.action. It will be one of an enum that can be seen in detail in the API reference.

Yet to cover:

  • Capabilities
  • Traits
  • UI

Built With Holochain

Please click "suggest an edit", and add something you built with Holochain to the list!

Building Holochain Apps: Zome Code

Adding a Zome

Capabilities

Entry Definitions

Read & Write Data Operations

Entry Validation

Genesis

Linking

Testing Functions

Preparing For App Packaging

Complete Zome API Reference

API App Variables

Note: Full reference is available in language-specific API Reference documentation.

For the Rust hdk, see here

Name Purpose
DNA_NAME Name of the Holochain DNA taken from the DNA.
DNA_HASH The hash of the DNA
AGENT_ID_STR The identity string used to initialize this Holochain
AGENT_ADDRESS The address (constructed from the public key) of this agent.
AGENT_INITIAL_HASH The hash of the first identity entry on the local chain.
AGENT_LATEST_HASH The hash of the most recent identity entry that has been committed to the local chain.

Zome API Functions

Overview

A Zome API Function is any Holochain core functionality that is exposed as a callable function within Zome code.

Compare this to a Zome Callback Function, which is implemented by the Zome code and called by Holochain.

So, Zome functions (functions in the Zome code) are called by Holochain, which can optionally call Zome API Functions, and then finally return a value back to Holochain.

Holochain blocks
  -> calls Zome function
  -> executes WASM logic compiled from Zome language
  -> Zome logic calls zome API function
    -> Holochain natively executes Zome API function
    -> Holochain returns value to Zome function
  -> Zome function returns some value
  -> Holochain receives final value of Zome function

Each Zome API Function has a canonical name used internally by Holochain.

Zome code can be written in any language that compiles to WASM. This means the canonical function name and the function name in the Zome language might be different. The Zome language will closely mirror the canonical names, but naming conventions such as capitalisation of the zome language are also respected.

For example, the canonical verify_signature might become verifySignature in AssemblyScript.

When a Zome API function is called from within Zome code a corresponding Rust function is called. The Rust function is passed the current Zome runtime and the arguments that the zome API function was called with. The Rust function connects Zome logic to Holochain core functionality and often has side effects. The return value of the Rust function is passed back to the Zome code as the return of the Zome API function.

Property

Canonical name: property

Returns an application property, which are defined by the developer in the DNA. It returns values from the DNA file that you set as properties of your application (e.g. Name, Language, Description, Author, etc.).

Entry Address

Canonical name: entry_address

Returns the address that a given entry will hash into.

LINK

Debug

Canonical name: debug

Debug sends the passed arguments to the log that was given to the Holochain instance and returns None.

LINK

Call

Canonical name: call

Perform a function call to an exposed function from another Zome.

LINK

Sign

Canonical name: sign

Not yet available, but you will see updates here:

LINK

Verify Signature

Canonical name: verify_signature

Not yet available, but you will see updates here:

LINK

Commit Entry

Canonical name: commit_entry

Attempts to commit an entry to your local source chain. The entry will have to pass the defined validation rules for that entry type. If the entry type is defined as public, will also publish the entry to the DHT. Returns either an address of the committed entry as a string, or an error.

LINK

Update Entry

Canonical name: update_entry

Not yet available, but you will see updates here:

LINK

Update Agent

Canonical name: update_agent

Not yet available, but you will see updates here:

LINK

Remove Entry

Canonical name: remove_entry

Not yet available, but you will see updates here:

LINK

Get Entry

Canonical name: get_entry

Given an entry hash, returns the entry from the DHT if that entry exists.

Entry lookup is done in the following order:

  • The local source chain
  • The local hash table
  • The distributed hash table

Caller can request additional metadata on the entry such as type or sources (hashes of the agents that committed the entry).

LINK

Get Links

Canonical name: get_links

Not yet available, but you will see updates here:

LINK

Link Entries

Canonical name: link_entries

Consumes three values, two of which are the addresses of entries, and one of which is a string that defines a relationship between them, called a tag. Later, lists of entries can be looked up by using get_links. Entries can only be looked up in the direction from the base, which is the first argument, to the target, which is the second.

LINK

Remove Entry

Canonical name: remove_entry

Not yet available, but you will see updates here:

LINK

Query

Canonical name: query

Returns a list of addresses of entries from your local source chain, that match a given type. You can optionally limit the number of results.

LINK

Send

Canonical name: send

Not yet available, but you will see updates here:

LINK

Start Bundle

Canonical name: start_bundle

Not yet available, but you will see updates here:

LINK

Close Bundle

Canonical name: close_bundle

Not yet available, but you will see updates here:

LINK

Callback functions

Overview

A callback function is implemented in the Zome language and called by Holochain.

Contrast this to a Zome API function that is implemented by Holochain and called by the Zome.

As per Zome API functions, the names of the callback functions may be slightly different depending on the language. The canonical name follows Rust naming conventions but other languages may vary these (e.g. camel casing).

To implement a callback function in a Zome simply define it and Holochain will call it automatically during standard internal workflows.

Reference

Genesis

Canonical name: genesis Parameters: none

Called the first time an agent launches an instance of a DNA with Holochain. Within genesis an app develop has the ability whether the given agent should be allowed to successfully join the Holochain network for this particular DNA, by implenting rules, or preconditions that must be met. If genesis comes back from the Zome with a fail, the agent will not be able to join.

View in Rust hdk

Node to Node Messaging

Cryptographic Signatures

Calling Other Zomes

Bundling

Emitting Signals

Building Holochain Apps: User Interfaces

Web UIs with Websockets

QML based UIs

Building Holochain Apps: Packaging And Testing

Packaging Your App Into DNA

Running Apps

Scenario Testing

Going Live with Holochain Apps

Creating Versioned Releases

Building Holochain Apps: Advanced Topics

Building For Android

Note: These instructions for building Holochain on Android are adapted from here.

In order to get to libraries that can be linked against when building HoloSqape for Android, you basically just need to setup up according targets for cargo.

Given that the Android SDK is installed, here are the steps to setting things up for building:

  1. Install the Android tools:

    a. Install Android Studio b. Open Android Studio and navigate to SDK Tools: - MacOS: Android Studio > Preferences > Appearance & Behaviour > Android SDK > SDK Tools - Linux: Configure (gear) > Appearance & Behavior > System Settings > Android SDK c. Check the following options for installation and click OK: * Android SDK Tools * NDK * CMake * LLDB d. Get a beverage of your choice (or a full meal for that matter) why you wait for the lengthy download

  2. Setup ANDROID_HOME env variable:

On MacOS

export ANDROID_HOME=/Users/$USER/Library/Android/sdk

Linux: (assuming you used defaults when installing Android Studio)

export ANDROID_HOME=$HOME/Android/Sdk
  1. Create standalone NDKs (the commands below put the NDK in your home dir but you can put them where you like):
export NDK_HOME=$ANDROID_HOME/ndk-bundle
cd ~
mkdir NDK
${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 26 --arch arm64 --install-dir NDK/arm64
${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 26 --arch arm --install-dir NDK/arm
${NDK_HOME}/build/tools/make_standalone_toolchain.py --api 26 --arch x86 --install-dir NDK/x86
  1. Add the following lines to your ~/.cargo/config:
[target.aarch64-linux-android]
ar = "<your $HOME value here>/NDK/arm64/bin/aarch64-linux-android-ar"
linker = "<your $HOME value here>/NDK/arm64/bin/aarch64-linux-android-clang"

[target.armv7-linux-androideabi]
ar = "<your $HOME value here>/NDK/arm/bin/arm-linux-androideabi-ar"
linker = "<your $HOME value here>/NDK/arm/bin/arm-linux-androideabi-clang"

[target.i686-linux-android]
ar = "<your $HOME value here>/NDK/x86/bin/i686-linux-android-ar"
linker = "<your $HOME value here>/NDK/x86/bin/i686-linux-android-clang"

(this toml file needs absolute paths, so you need to prefix the path with your home dir).

  1. Now you can add those targets to your rust installation with:
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android

Finally, should now be able to build Holochain for Android with your chosen target, e.g.:

cd <holochain repo>
cargo build --target armv7-linux-androideabi --release

NOTE: there is currently a problem in that wabt (which we use in testing as a dev dependency) won't compile on android, and the cargo builder compiles dev dependencies even though they aren't being used in release builds. Thus as a work around, for the cargo build command above to work, you need to manually comment out the dev dependency section in both core/Cargo.toml and core_api/Cargo.toml

Extending Holochain

Embedding Holochain

Core API is a library for embedding a Holochain instance (an hApp) in your own code. So this is for the use case of writing your own software that runs hApps. A common use case might be "glueing" several hApps together or adding centralized services, like file storage, on top of a hApp.

Core API

Naming things

There are only two hard things in Computer Science: cache invalidation and naming things.

Rust naming conventions

If in doubt refer to the Rust conventions.

https://doc.rust-lang.org/1.0.0/style/style/naming/README.html

Holochain naming conventions

There are gaps where the Rust conventions are either silent or following them would make things too ambiguous.

Actions & reducers

  • Action is VerbNoun or Verb if there is no available noun and matches the underlying function e.g. GetEntry
  • ActionResponse is ActionName e.g. Action::GetEntry results in ActionResponse::GetEntry
  • reducer name is reduce_action_name e.g. reduce_get_entry

Actors & protocols

  • Actor Protocol is VerbNoun or Verb if there is no available noun and matches the underlying function e.g. PutEntry or Setup
  • Result of a Protocol is VerbNounResult or VerbResult e.g. PutEntryResult or SetupResult

Method names

  • method names that access something directly "for free" are the name of the thing being accessed, e.g. entry()
  • method names that have side effects or an expensive lookup are verb_noun() e.g. put_entry()

Short names

avoid micro names like t, e, h when table, entry, header is clearer.

avoid shorthand names like table when table_actor is clearer.

in the long run the legibility and unambiguity saves orders of magnitude more time than the typing costs.

Writing a Development Kit

The end goal of a Development Kit is to simplify the experience of writing Zomes that compile to WASM for Holochain apps.

At the time of writing, there is currently one active Developer Kit being written, for the Rust language. While it is possible to look at the Rust language HDK as a reference, this article is a more general guide that outlines what it takes to build a Development Kit.

If you are interested in supporting developers to write Zomes in an unsupported language, you will want to first of all check whether that language can be compiled to WebAssembly, as that is a requirement.

Why Development Kits

Development Kits are important because the WASM interface between Zomes and Holochain is really constrained. Because of WASMs design, WASM functions may only be called with 32 bit integers. Holochain implements a solution to this, but if app developers were to always have to interact with this solution directly, it would feel very complex. A Development Kit for each language should ideally be developed so that it gets so much simpler!

The Development Kit WASM Solution

To enable passing arguments more complex than 32 bit integers between Zomes and Holochain, a pattern of utilizing WASM memory is used. When it is running the WASM code for a Zome, Holochain has access to both read and write from the WASM memory.

The pattern defines that Holochain Zome API functions expect to both give and receive 32 bit integers which actually represent a WASM memory location. So to provide a Holochain Zome API function a complex argument, one must first write it into memory, and then call the function, giving it the memory location. Holochain will pull the argument from memory, execute its behaviour, store the result in memory, and return the memory location of the result. The Zome code then has to also lookup the result by its location in memory.

Technically, an app developer can do all of these things if they have a reason to, but most won't want to handle the extra step involving memory. A Development Kit, then, should handle the extra step of writing to memory, and calling the native API function, and reading the result from memory, and returning that instead. Plus a few other sprinkles on top.

Crafting the API

Using its WASM interpreter, Holochain exposes its callable Zome API functions by making them available as "imports" in Zome WASM modules. Per the memory discussion above, each of the Zome API functions have the same explicit function signature, but different implicit function signatures. The native functions have each been given a prefix so that Development Kit wrappers can expose a regular function name. Here is a complete list:

  • hc_debug
  • hc_call
  • hc_sign
  • hc_verify_signature
  • hc_commit_entry
  • hc_update_entry
  • hc_update_agent
  • hc_remove_entry
  • hc_get_entry
  • hc_link_entries
  • hc_query
  • hc_send
  • hc_start_bundle
  • hc_close_bundle

There is a special additional one called hc_init_globals which we will discuss further.

The Development Kit should implement and export one function per each native function from the list. The function should be called the same as its native form, but without the prefix. E.g. hc_update_agent should be called update_agent or updateAgent. That function should internally call the native function and handle the additional complexity around that.

In order to call these "external" functions, you will need to import them and provide their signature, but in a WASM import compatible way. In Rust, for example, this is simply:


# #![allow(unused_variables)]
#fn main() {
extern {
  fn hc_commit_entry(encoded_allocation_of_input: u32) -> u32;
}
#}

TODO: define or link to meaningful function signatures

Working with WASM Memory

The goal of the Development Kit is to expose a meaningful and easy to use version of the API functions, with meaningful arguments and return values. There is a bit of flexibility around how this is done, as coding languages differ. However, the internal process will be similar in nature. Here it is, generalized:

  1. declare, or use a passed, single page 64 KiB memory stack
  2. join whatever inputs are given into a single serializable structure
  3. serialize the given data structure as an array of bytes
  4. determine byte array length
  5. ensure it is not oversized for the stack
  6. allocate the memory
  7. write the byte array to memory
  8. create an allocation pointer for the memory
    a. use a 16 bit integer for the pointers offset
    b. use a 16 bit integer for the pointers length
  9. join the pointers into a single 32 bit integer
    a. high bits are offset
    b. low bits are length
  10. call the native function with that 32 bit integer and assign the result to another 32 bit integer
    a. e.g. encoded_alloc_of_result = hc_commit_entry(encoded_alloc_of_input)
  11. deconstruct that 32 bit integer into two variables
    a. use a 16 bit integer for the pointers offset
    b. use a 16 bit integer for the pointers length
  12. read string data from memory at the offset address
  13. deallocate the memory
  14. deserialize the string to JSON if JSON is expected

That looks like a lot of steps, but most of this code can be shared for the various functions throughout the Development Kit, leaving implementations to be as little as 5 lines long. Basically, the process inverts at the point of the native function call.

WASM Single Page Stack

TODO

App Globals

When writing Zome code, it is common to need to reference aspects of the context it runs in, such as the active user/agent, or the DNA hash of the app. Holochain exposes certain values through to the Zome, though it does so natively by way of the hc_init_globals function mentioned. Taking care to expose these values as constants will simplify the developer experience.

This is done by calling hc_init_globals with an input value of 0. The result of calling the function is a 32 bit integer which represents the memory location of a serialized JSON object containing all the app global values. Fetch the result from memory, and deserialize the result back into an object. If appropriate, set those values as exports for the Development Kit. For example, in Rust, values become accessible in Zomes using hdk::APP_NAME. It's recommended to use all capital letters for the export of the constants, but as they are returned as keys on an object from hc_init_globals they are in lower case. The object has the following values:

  • dna_name
  • dna_hash
  • agent_id_str
  • agent_address
  • agent_initial_hash
  • agent_latest_hash

See the API global variables page for details on what these are.

Publish It and Get In Touch

If you've made it through the process so far, good work. The community is an important part of the success of any project, and Holochain is no different. If you're really proud of your work, get in touch with the development team on the chat server, mention you're working on it, and request help if necessary. This book could be updated to include links to other HDKs. Whether you would like to, or you'd like the team to, the HDK could be published to the primary package manager in use for the language, to be used by developers around the world. For example, RubyGems for Ruby or npm for nodejs.

Zome implementation

Zome API functions

Each zome API function is implemented under nucleus::ribosome::api.

There is a fair bit of boilerplate at the moment, sorry!

To co-ordinate the execution of an API function across Rust and WASM we need to define a few related items.

Within nucleus::ribosome::api:

  • A variant in the ZomeApiFunction enum
  • The same canonical string in both as_str and from_str
  • A mapping to the API function under as_fn

As a new module under nucleus::ribosome::api:

  • A ribosome module implementing the invocation logic as invoke_*
  • A struct to hold/serialize any input args if needed

In ::action:

  • An action if the zome API function has side effects

Zome API function definition

Simply add the name of the new zome API function to the end of the enum.

Make sure to add the canonical names carefully. The Rust compiler will guide you through the rest if you miss something.

DO add a doc comment summarising what the zome function does and sketching the function signature.

DO extend the relevant unit tests.

Do NOT add to the start or middle of the enum as that will renumber the other zome functions.

Zome API function ribosome module

Each zome API function should have its own module under nucleus::ribosome::*.

Implement a public function as invoke_<canonical name>. The function must take two arguments, a &mut nucleus::ribosome::Runtime and a &wasmi::RuntimeArgs.

This function will be called by the invocation dispatch (see above).

Zome API function arguments

The wasmi::RuntimeArgs passed to the Zome API function contains only a single u32 value. This is an encoded representation of a single page of memory supported by the memory manager. The 16 high bits are the memory offset and the 16 low bits are the memory length. See the wasm_utils crate for more implementation details.

You don't have to work with the memory manager directly, simply pass the runtime and runtime args to nucleus::runtime_args_to_utf8 to get a utf-8 string from memory.

You DO have to handle serialization round trips if you want to pass anything other than a single utf-8 string to a zome API function.

The simplest way to do this is implement a struct that derives Serialize and Deserialize from serde, then use serde and .into_bytes() co-ordinate the round trip.

For an example implementation of a struct with several fields see:

  • nucleus::ribosome::commit::CommitArgs for the input args struct
  • nucleus::ribosome::commit::tests::test_args_bytes serializing the struct as bytes
  • nucleus::ribosome::commit::invoke_commit deserializing the struct from the runtime

Zome API function action dispatch

If the function has a side effect it must send an action to the state reduction layer.

Actions are covered in more detail in the state chapter.

In summary, if you want to send an action and wait for a return value:

  • create an outer channel in the scope of your invoke function that will receive the return value
  • call ::instance::dispatch_action_with_observer with:
    • the runtime's channels
    • the action the reducer will dispatch on
    • an observer sensor, which is a closure that polls for the action result and sends to your outer channel
  • block the outer channel until you receive the action result

Zome API function return values

The zome API function returns a value to wasm representing success or a wasm trap.

The success value can only be a single i32.

Traps are a low level wasm concern and are unlikely to be directly useful to a zome API function implementation.

See https://github.com/WebAssembly/design/blob/master/Semantics.md#traps

To get complex values out of wasm we use the memory manager, much like the input argument serialization (see above).

The util function nucleus::runtime_allocate_encode_str takes a string, allocates memory and returns the value that the zome API function must return.

To return an error relevant to holochain, return Ok with an HcApiReturnCode error enum variant.

For an example implementation returning a complex struct see:

  • agent::state::ActionResponse::GetEntry containing an Entry struct
  • nucleus::ribosome::get::invoke_get
    • match the action result against the correct enum variant
    • serialize the entry using serde
    • return the result of runtime_allocate_encode_str
    • if the action result variant does NOT match then return HcApiReturnCode::ErrorActionResult

Zome API function agent action

If the zome API function will cause side effects to the agent state then it must implement and dispatch an action.

Actions are covered in more detail in the state chapter.

In summary, if a new agent action (for example) is needed:

  • extend the action::Action enum
    • this sets the data type, the ActionWrapper provides a unique ID
    • use the canonical name if that makes sense
  • extend an ActionResult enum if the action has a return value
  • implement a reducer for the new action

State & Actions

Holochain uses a hybrid global/local state model.

In our bio mimicry terms the global state is for "short term memory" and local state wraps references to "long term memory".

The global state is implemented as Redux style reducers. Any module can dispatch an action to the global state. The action will be "reduced" to a new state tree value by the modules responsible for each branch of the state tree. The response values from a reduction must be polled directly from the state tree in a thread using a "sensor" closure in an observer.

Actions are stateless/immutable data structures that are dispatched by modules to communicate a request to do something potentially state changing. Everything in the system should be either stateless or change state only in response to an incoming action.

The global state is called "short term memory" because it is highly dynamic, readily inspectable, and volatile. It does not survive indefinitely and is best thought of as a cache of recent history.

Local state is implemented using actors to co-ordinate memory and threads in Rust for external, persistent state. The classic example is a database connection to the database that stores entries and headers. The db actor receives read/write messages, and a reference to the sender is stored in the global state.

Actions

The action module defines actions and action wrappers:

  • ActionWrapper: struct contains a unique ID for the action and the Action
  • Action: enum of specific data to a given action, e.g. Action::Commit

Processing an incoming action is a 3 step process:

  1. Implement reduce to resolve and dispatch to a handler
  2. Resolve the action to an appropriate handler
  3. Implement handler logic

Reduce

The reduce implementation is essentially copypasta. It handles resolving and dispatching to a handler with a new state clone. The handler resolution and dispatch logic should be split to facilitate clean unit testing.


# #![allow(unused_variables)]
#fn main() {
pub fn reduce(
    old_state: Arc<FooState>,
    action_wrapper: &ActionWrapper,
    action_channel: &Sender<ActionWrapper>,
    observer_channel: &Sender<Observer>,
) -> Arc<AgentState> {
  let handler = resolve_action_handler(action_wrapper);
  match handler {
      Some(f) => {
          let mut new_state: FooState = (*old_state).clone();
          f(&mut new_state, &action_wrapper, action_channel, observer_channel);
          Arc::new(new_state)
      }
      None => old_state,
  }
}
#}

Resolve an appropriate handler

The action handler should map signals to action handlers.


# #![allow(unused_variables)]
#fn main() {
fn resolve_action_handler(
    action_wrapper: &ActionWrapper,
) -> Option<fn(&mut AgentState, &ActionWrapper, &Sender<ActionWrapper>, &Sender<Observer>)> {
    match action_wrapper.action() {
        Action::Commit(_, _) => Some(handle_commit),
        Action::Get(_) => Some(handle_get),
        _ => None,
    }
}
#}

Implement the handlers

Each handler should respond to one action signal and mutate the relevant state.

The standard pattern is to maintain a HashMap of incoming action wrappers against the result of their action from the perspective of the current module. Each action wrapper has a unique id internally so there will be no key collisions.


# #![allow(unused_variables)]
#fn main() {
fn handle_foo(
    state: &mut FooState,
    action_wrapper: &ActionWrapper,
    _action_channel: &Sender<ActionWrapper>,
    _observer_channel: &Sender<Observer>,
) {
    let action = action_wrapper.action();
    let bar = unwrap_to!(action => Action::Bar);

    // do something with bar...
    let result = bar.do_something();

    state
        .actions
        .insert(action_wrapper.clone(), ActionResponse::Bar(result.clone()));
}
#}

WARNING: Actions are reduced in a simple loop. Holochain will hang if you dispatch and block on a new action while an outer action reduction is also blocking, waiting for a response.

Global state

instance::Instance has a state::State which is the one global state. Each stateful module has a state.rs module containing sub-state slices.

See src/agent/state.rs and src/nucleus/state.rs and how they are put together in src/state.rs.

State is read from the instance through relevant getter methods:


# #![allow(unused_variables)]
#fn main() {
instance.state().nucleus().dna()
#}

and mutated by dispatching an action:


# #![allow(unused_variables)]
#fn main() {
let entry = Entry::new( ... );
let action_wrapper = ActionWrapper::new(&Action::Commit(entry));
instance.dispatch(action_wrapper);
#}

Instance calls reduce on the state with the next action to consume:


# #![allow(unused_variables)]
#fn main() {
pub fn consume_next_action(&mut self) {
    if self.pending_actions.len() > 0 {
        let action = self.pending_actions.pop_front().unwrap();
        self.state = self.state.clone().reduce(&action);
    }
}
#}

The main reducer creates a new State object and calls the sub-reducers:


# #![allow(unused_variables)]
#fn main() {
pub fn reduce(&mut self, action_wrapper: &ActionWrapper) -> Self {
    let mut new_state = State {
        nucleus: ::nucleus::reduce( ... ),
        agent: ::agent::reduce( ... )
    }

    new_state.history.insert(action_wrapper);
    new_state
}
#}

Each incoming action wrapper is logged in the main state history to facilitate testing and "time travel" debugging.

Sub-module state slices are included in state::State as counted references.

The sub-module reducer must choose to either:

  • If mutations happen, return a cloned, mutated state slice with a new reference
  • If no mutations happen, return the reference to the original state slice

The reduce copypasta above demonstrates this as the possible return values.

Redux in Rust code was used as a reference from this repository.

Local state

Coming Soon.

@TODO @see https://github.com/holochain/holochain-rust/issues/176

Internal actors

Actors are discussed in two context:

  • Each Holochain agent as an actor in a networking context
  • Riker actors as an implemenation detail in the Holochain core lib

This article is about the latter.

Actor model

The actor model is a relatively safe approach to co-ordinating concurrency.

At a high level:

  • An actor is the "primitive", like objects are the primitive of the OO paradigm
  • Actors are stateful but this state is never exposed to the rest of the system
  • Actors manage their internal state
  • Actors maintain a message queue or "inbox"
  • Messages can be received concurrently but must be processed sequentially in FIFO order
  • The messages have a preset format
  • Actors update their internal state in response to messages
  • Actors can send messages to each other
  • Messages are always processed at most once
  • Actors can "supervise" each other to create a fault tolerent system
  • A supervisor can restart or stop a failed actor, or escalate the decision to another supervisor

The guarantees provided by the message queue allow actors to use stateful logic that would not be safe otherwise in a concurrent context.

For example, we can implement logic that reads/writes to the file system without locks or other co-ordination. Then put an actor in front of this logic and only interact with the file system through the relevant actor.

Riker

Riker is an actor library for Rust.

The actor implementation in Riker has a few key concepts:

  • protocol: a set of valid messages that can be sent (e.g. an enum)
  • actor system: manages and co-ordinates all actors
  • actor: anything implementing the Actor trait to create new actor instances and handle receiving messages
  • actor instance: an instance of the actor struct that has internal state and is tracked by the actor system
  • actor ref(erence): an ActorRef that can tell messages to the actor instance it references via. the actor system

The actor reference is a "killer feature" of Riker for us.

  • known size at compile, safe as properties of structs/enums
  • small size, almost free to clone
  • safe to share across threads and copy, no Arc reference counting, no locks, etc.
  • safe to drop (the actor system maintains a URI style lookup)
  • known type, no onerous generic trait handling
  • no onerous lifetimes

Frequently Asked Questions

  1. How is Holochain different from blockchain?
  2. Why do you call it "Holochain"?
  3. How is Holochain different from a DHT (Distributed Hash Table)?
  4. What kind of projects is Holochain good for? What is Holochain not good for?
  5. What is Holochain's consensus algorithm?
  6. Can you run a cryptocurrency on Holochain?
  7. How is Holochain different from __________?
  8. What language is Holochain written in? What languages can I use to make Holochain apps?
  9. Is Holochain open source? 10 How is Holochain more environmentally ethical than blockchain?
  10. How are data validated on Holochain?
  11. What happens to data when a node leaves the network?
  12. Should I build my coin/token on Holochain?
  13. What does “agent-centric” mean? How is this different from “data-centric”?
  14. What is the TPS (Transactions Per Second) on Holochain?

How is Holochain different from blockchain?

Holochain and blockchain are built for fundamentally different use cases. Blockchain is relatively good for systems where it’s absolutely necessary to maintain global consensus. Holochain is much better than blockchain at anything that requires less than universal consensus (most things): It’s faster, more efficient, more scalable, adaptable, and extendable.

Long before blockchains were hash chains and hash trees. These structures can be used to ensure tamper-proof data integrity as progressive versions or additions to data are made. These kinds of hashes are often used as reference points to ensure data hasn't been messed with—like making sure you're getting the program you meant to download, not some virus in its place.

Instead of trying to manage global consensus for every change to a huge blockchain ledger, every participant has their own signed hash chain (countersigned for transactions involving others). After data is signed to local chains, it is shared to a DHT where every node runs the same validation rules (like blockchain nodes all run the same validation rules. If someone breaks those rules, the DHT rejects their data—their chain has forked away from the holochain.

The initial Bitcoin white paper introduced a blockchain as an architecture for decentralized production of a chain of digital currency transactions. This solved two problems (time/sequence of transactions, and randomizing who writes to the chain) with one main innovation of bundling transactions into blocks which somebody wins the prize of being able to commit to the chain if they solve a busywork problem faster than others.

Now Bitcoin and blockchain have pervaded people's consciousness and many perceive it as a solution for all sorts of decentralized applications. However, when the problems are framed slightly differently, there are much more efficient and elegant solutions (like holochains) which don't have the processing bottlenecks of global consensus, storage requirements of everyone having a FULL copy of all the data, or wasting so much electricity on busywork.

Why do you call it "Holochain"?

A variety of reasons: it's a composed whole of other technologies, it's structurally holographic, and it empowers holistic patterns.

A unified cryptographic whole

Holochain is made from multiple cryptographic technologies composed into a new whole.

  • Hashchains: Hashchains provide immutable data integrity and definitive time sequence from the vantage point of each node. Technically, we're using hash trees—blockchains do too, but they're not called blocktrees, so we're not calling these holotrees.

  • Cryptographic signing of chains, messages, and validation confirmations maintain authorship, provenance, and accountability. Countersigning of transactions/interactions between multiple parties provide non-repudiation and "locking" of chains.

  • DHT (Distributed Hash Table) leverages cryptographic hashes for content addressable storage, while randomizing of interactions by hashing into neighborhoods to impede collusion, and processing validation #1 and #2 to store data on the DHT.

Holographic storage

Every node has a resilient sample of the whole. Like cutting a hologram, if you were to cut a Holochain network in half (make it so half the nodes were isolated from the other half), you would have two whole, functioning systems, not two partial, broken systems.

This seems to be the strategy used to create resilience in natural systems. For example, where is your DNA stored? Every cell carries its own copy, with different functions expressed based on the role of that cell.

Where is the English language stored? Every speaker carries it. People have different areas of expertise, or exposure to different slang or specialized vocabularies. Nobody has a complete copy, nor is anyone's version exactly the same as anyone else, If you disappeared half of the English speakers, it would not degrade the language much.

If you keep cutting a hologram smaller and smaller eventually the image degrades enough to stop being recognizable, and depending on the resiliency rules for DHT neighborhoods, holochains would likely share a similar fate. Although, if the process of killing off the nodes was not instantaneous, the network may be able to keep reshuffling data per redundancy requirements to keep it alive.

Holarchy

Holochains are composable with each other into new levels of unification. In other words, Holochains can build on decentralized capacities provided by other Holochains, making new holistic patterns possible. Like bodies build new unity on holographic storage patterns that cells use for DNA, and a society build new unity on the holographic storage patterns of language, and so on.

How is Holochain different from a DHT (Distributed Hash Table)?

DHTs enable key/value pair storage and retrieval across many machines. The only validation rules they have is the hash of the data itself to confirm what you're getting is probably what you intended to get. They have no other means to confirm authenticity, provenance, timelines, or integrity of data sources.

In fact, since many DHTs are used for illegal file sharing (Napster, Bittorrent, Sharezaa, etc.), they are designed to protect anonymity of uploaders so they won't get in trouble. File sharing DHTs frequently serve virus infected files, planted by uploaders trying to infect digital pirates. There's no accountability for actions or reliable way to ensure bad data doesn't spread.

By embedding validation rules as a condition for the propagation of data, our DHT keeps its data bound to signed source chains. This can provide similar consistency and rule enforcement as blockchain ledgers asynchronously so bottlenecks of immediate consensus become of the thing of the past.

The DHT leverages the signed source chains to ensure tamper-proof immutability of data, as well as cryptographic signatures to verify its origins and provenance.

The Holochain DHT also emulates aspects of a graph database by enabling people to connect links to other hashes in the DHT tagged with semantic markers. This helps solve the problem of finding the hashes that you want to retrieve from the DHT. For example, if I have the hash of your user identity, I could query it for links to blogs you've published to a holochain so that I can find them without knowing either the hash or the content. This is part of how we eliminate the need for tracking nodes that many DHTs rely on.

What kind of projects is Holochain good for?

Sharing collaborative data without centralized control. Imagine a completely decentralized Wikipedia, DNS without root servers, or the ability to have fast reliable queries on a fully distributed PKI, etc.

  • Social Networks, Social Media & VRM: You want to run a social network without a company like Facebook in the middle. You want to share, post, publish, or tweet to shared space, while automatically keeping a copy of these things on your own device.

  • Supply Chains & Open Value Networks: You want to have information that crosses the boundaries of companies, organizations, countries, which is collaboratively shared and managed, but not under the central control of any one of those organizations.

  • Cooperatives and New Commons: You want to create something which is truly held collectively and not by any particular individual. This is especially good for digital assets.

  • P2P Platforms: Peer-to-Peer applications where every person has similar capabilities, access, responsibilities, and value is produced collectively.

  • Collective Intelligence: Governance, decision-making frameworks, feedback systems, ratings, currencies, annotations, or work flow systems.

  • Collaborative Applications: Chats, Discussion Boards, Scheduling Apps, Wikis, Documentation, etc.

  • Reputational or Mutual Credit Cryptocurrencies: Currencies where issuance can be accounted for by actions of peers (like ratings), or through double-entry accounting are well-suited for holochains. Fiat currencies where tokens are thought to exist independent of accountability by agents are more challenging to implement on holochains.

What is Holochain not good for?

You probably should not use Holochain for:

  • Just yourself: You generally don't need distributed tools to just run something for yourself. The exception would be if you want to run a holochain to synchronize certain data across a bunch of your devices (phone, laptop, desktop, cloud server, etc.)

  • Anonymous, secret, or private data: Not only do we need to do a security audit of our encryption and permissions, but you're publishing to a shared DHT space, so unless you really know what you're doing, you should not assume data is private. Some time in the future, I'm sure some applications will add an anonymization layer (like TOR), but that is not native.

  • Large files: Think of holochains more like a database than a file system. Nobody wants to be forced to load and host your big files on their devices just because they are in the neighborhood of its hash. Use something like IPFS if you want a decentralized file system.

  • Data positivist-oriented apps: If you have built all of your application logic around the idea that data exists as an absolute truth, not as an assertion by an agent at a time, then you would need to rethink your whole approach before putting it in a Holochain app. This is why most existing cryptocurrencies would need significant refactoring to move from blockchain to Holochain, since they are organized around managing the existence of cryptographic tokens.

What is Holochain's consensus algorithm?

Holochains don't manage consensus, at least not about some absolute perspective on data or sequence of events. They manage distributed data integrity. Holochains do rely on consensus about the validation rules (DNA) which define that integrity, but so does every blockchain or blockchain alternative (e.g. Bitcoin Core). If you have different validation rules, you're not on the same chain. These validation rules establish the "data physics," and then applications are built on that foundation.

In making Holochain, our goal is to keep it "as simple as possible, but no simpler" for providing data integrity for fully distributed applications. As we understand it, information integrity does not require consensus about an absolute order of events. You know how we know? Because the real world works this way—meaning, the physically distributed systems outside of computers. Atoms, molecules, cells, bodies each maintain the integrity of their individual and collective state just fine without consensus on a global ledger.

Not only is there no consensus about an absolute order of events, but if you understand the General Theory of Relativity, then you'll understand there is in fact no real sequence of events, only sequences relative to a particular vantage point.

That's how holochains are implemented. Each source chain for each person/agent/participant in a Holochain preserves the immutable data integrity and order of events of that agent's actions from their vantage point. As data is published from a source chain to the validating DHT, then other agents sign their validation, per the shared "physics" encoded into the DNA of that Holochain.

The minor exception to the singular vantage point of each chain, is the case when a multi-party transaction is signed to each party's chain. That is an act of consensus -- but consensus on a very small scale -- just between the parties involved in the transaction. Each party signs the exact same transaction to with links to each of their previous chain entries. Luckily, it's pretty easy to reach consensus between 2 or 3 parties. In fact, that is already why they're doing a transaction together, because they all agree to it.

Holochains do sign every change of data and timestamp (without a universal time synchronization solution), This provides ample foundation for most applications which need solid data integrity for shared data in a fully distributed multi-agent system. Surely, there will be people who will build consensus algorithms on top of foundation (maybe like rounds, witnesses, supermajorities of Swirld),

However, if your system is designed around data having one absolute true state, not one which is dynamic and varied based on vantage point, we would suggest you rethink your design. So far, for every problem space where people thought they needed an absolute sequence of events or global consensus, we have been able to map an alternate approach without those requirements. Also, we already know this is how the world outside of computers works, so to design your system to require (or construct) an artificial reality is probably setting yourself up for failure, or at the very least for massive amounts of unnecessary computation, communication, and fragility within your system.

How is Holochain more environmentally ethical than blockchain?

Holochain removes the need for global consensus, and with it the expenditure of massive amounts of electricity to synchronize millions of nodes about data that aren't relevant to them.

There are two reasons Holochain is vastly more efficient than blockchain and more ethical in a green sense:

  1. It eliminates the need for all nodes to be synchronized with each other in global consensus. Sharding is usually enabled on Holochain. This means that when two nodes make a transaction, each node saves a countersigned record of that transaction. Additionally, the transaction is published to the Distributed Hash Table (sent to and saved by some unpredictably random nodes that can be looked up later for retrieval).

    Sharding is configurable by app, and in some cases it's a good idea to turn it off. For example, imagine a distributed Slack-like team messaging app. With only 40-50 members, full synchronization would be worth the extra bandwidth requirement for the benefit of offline messages and reduced load times. But for most applications, global synchronization isn't really needed and sharding is kept on.

    Because of DHTs, and the sharding they enable, Holochain actually doesn't rely on the transfer of large amounts of redundant information, and uses vastly less bandwidth than blockchain.

  2. There's no mining on Holochain. Blockchain's proof-of-work system provides a hefty incentive for thousands of people to spend the processing power of their CPUs and GPUs using up huge amounts of electricity on solving a meaningless cryptographic puzzle. Holochain doesn't have mining.

How is Holochain different from __________?

TODO: Update with reference to Rust project.

Please see the Comparisons page.

What language is Holochain written in? What languages can I use to make Holochain apps?

Holochain is written in the Rust programming language. At a low level, Holochain runs WebAssembly code, but for all practical purposes developers will write applications in a language that compiles to WebAssembly such as Rust, C, C++, Go, etc. For now, only Rust has tier 1 support for writing apps, because it has a "Holochain Development Kit" library which makes writing WebAssembly apps easy.

Is Holochain open source?

Yes, it has an open source license.

Can you run a cryptocurrency on Holochain?

Theoretically, yes—but for the moment, we'd discourage it.

If you don't know how to issue currencies through mutual credit, or how to account for them through double entry accounting, then you probably shouldn't build one on Holochain. If you do understand those key principles, than it is not very difficult to build a cryptocurrency for which Holochain provides ample accounting and data integrity.

However, you probably shouldn't try to do it in the way everyone is used to building cryptocurrencies on a global ledger of cryptographic tokens. Determining the status of tokens/coins is what create the need for global consensus (about the existence/status/validity of the token or coin). However, there are other approaches to making currencies which, for example, involve issuance via mutual credit instead of issuance by fiat.

Unfortunately, this is a hotly contested topic by many who often don't have a deep understanding of currency design nor cryptography, so we're not going to go too deep in this FAQ. We intend to publish a white paper on this topic soon, as well as launch some currencies built this way.

How are data validated on Holochain?

On Holochain, each node that receives a record of a transaction validates it against the shared application rules and gossips it to their peers. If the rules are broken, that transaction is rejected by the validator.

There is no overall, global "correctness" (or consensus) built in to Holochain. Instead, each node that receives a record of a transaction validates it against the shared application rules and gossips it to their peers. If the rules are broken, that transaction is rejected by the validator. If foul play is detected on a node's part (the node is either propagating or validating bad data) that node is blocked and a warning is sent to others. Here's an infographic describing this process. In summary, instead of a global consensus system, Holochain uses an accountability-based system with data validation by peers.

Applying this to the example of 'Ourbnb', an imaginary distributed version ofAirbnb: The Ourbnb Holochain app would certainly be written with a rule, "don't rent your apartment to two parties at the same time." So the moment a user rents to two parties at the same time, nodes receiving that datum on the DHT attempt to validate it against the app rules, detect a collision, and reject it. Holochain's gossip protocol is designed to operate at a rate at which collisions will be detected nearly immediately by gossiping peers. And since Holochain doesn't have a coin built into it, it incentivizes users to cooperate and co-create.

As a user, you don't need to trust the provider of the application you're using, only agree with the shared protocols that make up the application itself. Aside from being responsible for the maintenance and security of apps they provide, application providers on Holochain are not like traditional application providers today (think Facebook, Twitter, etc.). They don't host your data because your data is stored by you and a random subset of the users of the application.

What happens to data when a node leaves the network?

The DHT of a Holochain app makes sure that there are always enough nodes on the network that hold a given datum.

When people running Holochain apps turn off their device, they leave the network. What happens to their data and the data of other people they were storing? There are always enough nodes that hold a given piece of data in the network so as to prevent data loss when nodes leave. The DHT and Holochain gossip protocol are designed this way. Also, the redundancy factor of data on a given DHT is configurable so it can be fine-tuned for any purpose. For example, a chat app for a small team might set a redundancy factor of 100% in order to prevent long loading times, while an app with thousands of users might have a very small redundancy factor.

Should I build my coin/token on Holochain?

Since it's agent-centric instead of data-centric like traditional blockchains, Holochain isn't the best platform on which to build a token or coin.

The idea of tokens or coins is a direct representation of a system being data-centric. While theoretically it would be possible to create a token on Holochain, it would be taking a step back instead of a step forward. The more exciting possibility is creating mutual credit currencies on Holochain. These are agent-centric currencies that are designed to facilitate active exchange of value and flourishing ecosystems instead of hoarding.

What does “agent-centric” mean? How is this different from “data-centric?”

Agent-centric systems view data not as an object, but as a shared experience.

Traditional blockchains are data-centric: they rely on and are built around the concept that data is a thing—an object. Holochain transitions to agent-centricism: the idea that data is shared experiences seen from many points of view. It's not a thing. It's a collection of shared, relative experiences. Einstein discovered this about the physical world a hundred years ago—Relativity. So why are modern blockchains that are supposedly "cutting edge" still falling back on this antiquated idea that data is an object, and for two agents to have different views of one piece of data is wrong?

Holochain is deeply agent-centric. Using tech that embodies this mindset enables vastly richer interactions and collaboration to happen through its technology while at the same time being thousands of times more efficient.

What is the TPS (Transactions Per Second) on Holochain?

Holochain doesn't have a set TPS (transactions per second) like other blockchain-based or blockchain-derived projects might because there's central point through which all transactions must pass. Instead, Holochain is a generalized protocol for distributed computing.

It's common to ask a blockchain project, "How much can your technology handle? What's its TPS?" This is because nearly all of these projects are built around the limiting idea of a global ledger.

But you are not asking, how many posts per second Facebook can do. Why? Because there is no technical problem, adding more servers to Facebook's data center (only maybe monetary problems).

You are not asking how many emails per second the internet can handle, because there is no single bottleneck for email-sending, like there would be with a centralized approach.

Why are we seeing a transaction limit with blockchain networks? Because blockchain in a strange way marries a decentralized p2p network of nodes with the logical notion of one absolute truth, i.e. the blockchain being one big decentralized database of transactions. It tries to maintain this way of thinking about apps that we are used to from centralized servers. It forces every node into the same "consensus". That is implemented by having everybody share and validate everything. That does work, and maybe there are few usecases (like a global naming system maybe?) where it might be advantageous.. but applying that for everything is nonsensical.

Holochain is not forcing such a model. Instead it allows for building applications that are like email. The application is rather like a protocol, or grammar, or (I prefer this language) like a dance. If you know the dance (If you have a copy of the validation rules of the app) you can tell who else is dancing that dance and who is not. The difference between Holochain and something like email is that (similarly to blockhain) Holochain is applying 1. cryptographic signatures and 2. tamper proof hash-chains (hence Holochain) so that you can build a distributed system you can trust in. You know it is impossible (I'd rather say: very very hard) to game somebody. This so far was only possible by having trusted authorities like banks or Facebook.

So, Holochain as an app framework does not pose any limit of transactions per second because there is no place where all transactions have to go through. It is like asking, "how many words can humanity speak per second?" Well, with every human being born, that number increases. Same for Holochain.

Glossary

Agent

Keys

DNA

Zome

Source Chain

Distributed Hash Table

Local Hash Table

implementation details

First, read about state actors.

The 1:1 API implementation between actors and their inner table is achieved by internally blocking on an ask from riker patterns.

https://github.com/riker-rs/riker-patterns

The actor ref methods implementing HashTable sends messages to itself.

Calling table_actor_ref.commit(entry) looks like this:

  1. the actor ref constructs a Protocol::PutPair message including the entry
  2. the actor ref calls its own ask method, which builds a future using riker's ask
  3. the actor ref blocks on its internal future
  4. the referenced actor receives the Commit message and matches/destructures this into the entry
  5. the entry is passed to the commit() method of the inner table
  6. the actor's inner table, implementing HashTable, does something with commit (e.g. MemTable inserts into a standard Rust, in-memory HashMap)
  7. the return value of the inner table commit is inserted into a CommitResult message
  8. the CommitResult message is sent by the actor back to the actor ref's internal future
  9. the actor ref stops blocking
  10. the CommitResult message is destructured by the actor ref so that the return of commit satisfies the HashTable trait implementation

Riker ask returns a future from the futures 0.2.2 crate.

table_actor.block_on_ask() calls block_on and unwrap against this ask.

Both the block and the unwrap should be handled better in the future.


suggest an edit