Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zomes page #511

Merged
merged 27 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0252635
application structure -- overview page
pdaoust Jan 9, 2025
289785c
fix admonition markup
pdaoust Jan 9, 2025
b3bada9
comment out app structure submenu
pdaoust Jan 9, 2025
d4d8cf5
touch up app structure intro a bit
pdaoust Jan 9, 2025
530138f
proofread/tighten up app structure overview
pdaoust Jan 9, 2025
50705ca
ack, still didn't get those fences right
pdaoust Jan 9, 2025
881e9bf
more tightening of app structure intro
pdaoust Jan 9, 2025
f17a28d
Apply suggestions from code review
pdaoust Jan 10, 2025
3e0bc64
WIP
pdaoust Jan 10, 2025
73c283b
Merge branch 'feat/guide/app-structure' of github.com:holochain/docs-…
pdaoust Jan 10, 2025
6116a6a
add sub-nav to app structure TOC
pdaoust Jan 10, 2025
5d420bc
Merge branch 'feat/guide/app-structure' into feat/guide/app-structure…
pdaoust Jan 10, 2025
b494c32
small content fixes
pdaoust Jan 15, 2025
2861e74
add zomes page to nav
pdaoust Jan 15, 2025
c447f83
add instructions for creating zomes
pdaoust Jan 15, 2025
d69c6b1
fix broken/not-yet-existent links
pdaoust Jan 15, 2025
5fccf03
one more URL fix
pdaoust Jan 15, 2025
efd5be0
more?!
pdaoust Jan 15, 2025
1b328e3
basic overview of Holochain and hApps, defines some terms
pdaoust Jan 16, 2025
5872cf9
changes to zomes page based on PR review
pdaoust Jan 17, 2025
c46eb9e
Apply suggestions from code review
pdaoust Jan 17, 2025
2acbb7e
update language about zome structure
pdaoust Jan 17, 2025
729c51a
missed a few merge conflicts
pdaoust Jan 17, 2025
65fa939
test/fix all code samples and commands in zomes page
pdaoust Jan 17, 2025
54951f3
one more
pdaoust Jan 17, 2025
a87509c
improve instructions to cargo build the zomes
pdaoust Jan 17, 2025
897cb1e
add cdylib to spelling dict
pdaoust Jan 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cspell/words-that-should-exist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ affordances
automations
birthdate
chrono
composability
counterparties
counterparties'
counterparty
Expand All @@ -29,4 +30,5 @@ todo
todos
unforgeable
uninstallation
uninstantiated
unvalidated
3 changes: 3 additions & 0 deletions src/pages/_data/navigation/mainNav.json5
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
]
},
{ title: "Build", url: "/build/", children: [
{ title: "Application Structure", url: "/build/application-structure/", children: [
{ title: "Zomes", url: "/build/zomes/" },
zippy marked this conversation as resolved.
Show resolved Hide resolved
]},
{ title: "Working with Data", url: "/build/working-with-data/", children: [
{ title: "Identifiers", url: "/build/identifiers/" },
{ title: "Entries", url: "/build/entries/" },
Expand Down
89 changes: 89 additions & 0 deletions src/pages/build/application-structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
title: Application Structure
---

::: topic-list
### In this section {data-no-toc}

* Application Structure (this page)
* [Zomes](/build/zomes/) --- integrity vs coordinator, how to structure and compile
* DNAs (coming soon) --- what they're used for, how to specify and bundle
* hApps (coming soon) --- headless vs UI-based, how to bundle and distribute
:::

::: intro
There are a few basic units of composability and packaging you'll need to know about when you're structuring your hApp. Each has different purposes, and the way you break up your code makes a difference to how it works in terms of access, privacy, participant responsibilities, and code reuse.
:::

## Zomes, DNAs, and hApps

### Zome

The smallest unit in a hApp is called a **zome** (a play on DNA chromosomes). It's the actual binary code that runs in Holochain's [WebAssembly](https://webassembly.org/) sandbox.

!!! info Why WebAssembly?

We chose WebAssembly because:

* A [number of languages](https://github.com/appcypher/awesome-wasm-langs) can already be compiled to WebAssembly, which we hope will mean Holochain can support many languages on the back end in the future (currently we only supply a back-end SDK for Rust.)
* It's small and fast --- it can get compiled to machine code for near-native speed.
* Holochain is written in Rust, and Rust has an excellent WebAssembly engine called [Wasmer](https://wasmer.io/) that works on all the major operating systems.
* It provides a secure sandbox to run untrusted code within.

!!!

A zome has access to Holochain via the **host API** and also exposes functions of its own. Some of these functions are **callbacks** and some of them you invent yourself to create your back end's API.

There are two kinds of zome:

* An **integrity zome** defines a set of data types --- your application's schema --- and validation rules for operations that create, update, or delete data of those types; in other words, your data model.
* A **coordinator zome** defines a set of functions for interacting with data, peers, and other coordinator zomes.

Zomes are usually created as pairs --- an integrity zome that defines a data model and a coordinator zome that defines functions for operating on this model. You don't have to do it this way though; coordinator zomes don't need an integrity zome if they don't manipulate data, or they can depend on multiple integrity zomes, or multiple coordinators can depend on the same integrity zome.

If you mean for your zomes to be reused by other projects, you can share them via a public Git repository or [crates.io](https://crates.io) (tag your crates with `#holochain` so others can find them).
pdaoust marked this conversation as resolved.
Show resolved Hide resolved

[Read more on the Zomes page](/build/zomes/).

### DNA

One or more zomes are bundled into a **DNA**, including at least one integrity zome. When two or more agents install and run a DNA, a new peer-to-peer network is created among them to interact and store shared data.

**A DNA, and the network created for it, is uniquely defined by its integrity zomes, plus any modifiers.** The hash of the integrity zomes plus modifiers is called the **DNA hash**, and is the unique identifier for the network.

!!! info Why are coordinator zomes not included in the DNA hash?

Coordinator zomes are bundled with a DNA, but they don't contribute to its hash's uniqueness. That's because they don't constitute the 'rules of the game' for a network like integrity zomes do.

This means you can hot-swap coordinators as you fix bugs and add features, without changing the DNA hash and creating a new network, which we sometimes call **forking**. The only things that should cause a fork are changes to integrity code.

!!!

Because each DNA has its own separate peer network and data store, you can use the DNA concept to come up with creative approaches to [privacy](https://dialnet.unirioja.es/servlet/articulo?codigo=8036267) and access, separation of responsibilities, or data retention.

### hApp

One or more DNAs come together in a **hApp** (Holochain app). Each DNA fills a named **role** in the hApp, and you can think of it like a [microservice](https://en.wikipedia.org/wiki/Microservices).

Each agent generates their own public/private key pair when they install a hApp. Their public key acts as their **agent ID** which identifies them as a participant in all the networks created for all the DNAs in the hApp. When a DNA is activated, it's bound to this key pair and becomes a **cell**.

The hApp can specify two provisioning strategies for its DNAs:

* A cell can be instantiated at app installation time.
* A new cell can be **cloned** from an existing DNA at any time _after the hApp is installed_, with an optional limit on the number of clones.

A hApp can optionally include a web-based UI that supporting Holochain runtimes <!-- TODO: link --> can serve to the user.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think runtimes deserves its own introduction/definition. I think this term is not accurate to the standard meaning of the term and is something we've fallen into using internally. I'd probably put it in bold like the other Holochain defined terms and give it a quick explanation

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do people normally define 'runtime'? I know I've personally fallen into using it, mostly externally, for lack of a more approachable word than 'conductor'.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just define this by example in our case and point to various runtimes (whenever we actually add this).


!!! info A hApp always runs locally

The big difference with peer-to-peer stacks like Holochain is that **all the code** --- both the back end and the front end --- **runs on the devices of the participants themselves**.

That means that a DNA doesn't exist as some piece of code that runs 'out there somewhere' --- instead it runs from the perspective of an individual. DNA + agent = cell.

There can still be bots or system-level services that do automated tasks. Those functions just have to be handled by one of the agents, and that agent doesn't have to be a human.

!!!

## Further reading

* Core Concepts: [Application Architecture](/concepts/2_application_architecture)
21 changes: 19 additions & 2 deletions src/pages/build/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,28 @@ This guide is under construction. Expect more content to be rapidly published in
This Build Guide organizes everything you need to know about developing Holochain applications into individual topics. Each topic page stands alone as a comprehensive guide to using a given feature or implementing a given functionality. There are lots of code examples which make it clear how to do something yet are generic enough to be universally useful. These examples may not cover every single use case, though, so we'll point you to the reference documentation often.
:::

## Working with data
## Holochain: a very brief overview

Holochain is a framework for building **peer-to-peer applications**, in which every participant runs the application code on their own device and connects directly to other participants (called their **peers**) to exchange data.

Holochain's first basic concept is the **agent**, which represents a human or automated participant in a peer-to-peer network. An agent identifies themselves with a self-generated public/private key pair, called their **agent ID**.

Holochain's second basic concept is the **DNA**, which is a chunk of application back-end code. When you write a Holochain application (which we call a **hApp**), you're writing code that runs sandboxed in a Holochain runtime (called a **conductor**) and responds to function calls from Holochain itself or from external processes such as a UI, system service, or other DNA on the same device, or another peer on the same network.

When an agent ID is bound to a **DNA**, the live DNA instance is called a **cell** and forms a network with other cells running the same DNA. This network is separate from all other networks formed by other DNAs.

Now that you've got some basic concepts and the terms we use for them, it's time to dive into application development.

## Application structure

::: topic-list
### Topics {data-no-toc}
* [Overview](/build/application-structure/) --- an overview of Holochain's modularity and composability units
* [Zomes](/build/zomes/) --- integrity vs coordinator, how to structure and compile
:::

## Working with data

::: topic-list
* [Overview](/build/working-with-data/) --- general concepts related to working with data in Holochain
* [Identifiers](/build/identifiers) --- working with hashes and other unique IDs
* [Entries](/build/entries/) --- defining, creating, reading, updating, and deleting data
Expand Down
141 changes: 141 additions & 0 deletions src/pages/build/zomes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
---
title: "Zomes"
---

::: intro
A **zome** (short for chromosome) is a module of executable code within a [**DNA**](/resources/glossary/#dna). It's the smallest unit of modularity in a Holochain application.
:::

## How a zome is structured

A zome is just a [WebAssembly module](https://webassembly.github.io/spec/core/syntax/modules.html) that exposes public functions. The **conductor** (the Holochain runtime) calls these functions at different points in the application's lifetime. Some functions have special names and serve as **callbacks** that are called by the Holochain system. Others are ones you define yourself, and they become your zome's API that external processes such as a UI can call.

## How a zome is written

We're focusing on Rust as a language for writing zomes, mainly because Holochain is written in Rust, so types can be shared between the host and zomes.

A Rust-based zome is a library crate that's compiled to the WebAssembly build target. We've created an SDK called the [Holochain Development Kit (HDK)](https://crates.io/crates/hdk/), which lets you define functions, exchange data with the **host** (the Holochain conductor), and access all of the host's functionality.

## The two types of zomes

### Integrity

An **integrity zome** defines a portion of your application's data model. This includes not just the structure of the data but also _validation rules for operations that manipulate this data_.

!!! info Keep your integrity zomes small

When you're writing an integrity zome, use the smaller [`hdi`](https://crates.io/crates/hdi) crate instead of `hdk`, because it's a subset of the HDK's functionality that contains everything an integrity zome needs. There's a lot of functionality in `hdk` that can't be used in an integrity zome's callbacks, and we recommend against putting anything in your integrity zome other than your data model. `hdi` is also more stable than `hdk`. Both of these things matter because every change to an integrity zome, including dependency updates, [changes the DNA hash](/build/application-structure/#dna), creating a new empty network and database.

!!!

<!-- TODO: placeholder to ask the question, should we mention the pattern of defining all your types in a separate crate so the coordinator can import the types without having to import the validation callback etc? is that possible? how does it work? -->

Your integrity zome tells Holochain about the types of [entries](/build/entries/) and [links](/build/links-paths-and-anchors/) it defines with macros called [`hdk_entry_types`](https://docs.rs/hdi/latest/hdi/attr.hdk_entry_types.html) and [`hdk_link_types`](https://docs.rs/hdi/latest/hdi/attr.hdk_link_types.html) added to enums of all the entry and link types. These create callbacks that are run at DNA install time. Read more in [Define an entry type](/build/entries/#define-an-entry-type) and [Define a link type](/build/links-paths-and-anchors/#define-a-link-type).

Finally, your integrity zome defines validation callbacks <!-- TODO: uncomment once lifecycle events PR is merged [validation callbacks](/build/lifecycle-events-and-callbacks/#define-a-validate-callback)--> that check for correctness of data and actions. Holochain runs this on an agent's own device when they try to author data, and when they're asked to store and serve data authored by others.

#### Create an integrity zome

**The easy way to create an integrity zome** is to [scaffold a new hApp](/get-started/3-forum-app-tutorial/). The scaffolding tool will generate all the project files, including scripts to test and build distributable packages, and it can also scaffold boilerplate code for all your app's required callbacks and data types.

If you want to create a zome without the scaffolding tool, first make sure you have Rust, Cargo, and the `wasm32-unknown-unknown` Rust build target installed on your computer. Then create a library crate:

```bash
cargo new my_integrity_zome --lib
```

Then add some necessary dependencies to your new `Cargo.toml` file:

```diff:toml
[dependencies]
+hdi = "=0.5.0-rc.1"
+serde = "1.0"
```

Now you can write a `validate` callback <!-- TODO: uncomment once lifecycle events PR is merged [`validate` callback](/build/lifecycle-events-and-callbacks/#define-a-validate-callback)--> and [define some entry types](/build/entries/#define-an-entry-type).

When you've written some code, compile your zome using `cargo`:

```bash
cargo build --release --target wasm32-unknown-unknown
```

### Coordinator

Coordinator zomes hold your back-end logic --- the functions that read and write data or communicate with peers. In addition to some optional lifecycle callbacks <!-- TODO: uncomment once lifecycle events PR is merged [lifecycle callbacks](/build/lifecycle-events-and-callbacks/#coordinator-zomes)-->, you can also write your own **zome functions** that serve as your zome's API.

#### Create a coordinator zome

Again, **the easiest way to create a coordinator zome** is to let the scaffolding tool do it for you. But if you want to do it yourself, it's the same as an integrity zome, with one exception. Your `Cargo.toml`'s dependencies should be modified like this:

```diff:toml
[dependencies]
-hdi = "=0.5.0-rc.1"
+hdk = "=0.4.0-rc.1"
serde = "1.0"
```

## Define a function

You expose a callback or zome function to the host by making it a `pub fn` and adding a macro called [`hdk_extern`](https://docs.rs/hdk/latest/hdk/prelude/attr.hdk_extern.html). This handles the task of passing data back and forth between the host and the zome, which is complicated and involves pointers to shared memory.

A zome function must have a **single input parameter** of any type and return an [`ExternResult<T>`](https://docs.rs/hdk/latest/hdk/map_extern/type.ExternResult.html), where `T` is also any type. The input parameter's type must be deserializable by [serde](https://serde.rs/), and the wrapped return value's type must be serializable. All of the example functions in this guide follow those constraints.

Callbacks are the same, except that they must also use the proper input and output types for the callback's signature.

Here's a very simple zome function that takes a name and returns a greeting:

```rust
use hdk::prelude::*;

#[hdk_extern]
pub fn say_hello(name: String) -> ExternResult<String> {
Ok(format!("Hello {}!", name))
}
```

### Handling errors

You can handle most errors in a function with the `?` short-circuit operator; the HDK does a good job of converting most of its own error types into `ExternResult<T>` and providing the zome name and the line number where the failure happened.

```rust
use hdk::prelude::*;

#[hdk_extern]
pub fn get_any_record(hash: AnyDhtHash) -> ExternResult<Option<Record>> {
// Short-circuit any error that `get` might return.
let maybe_record = get(hash, GetOptions::network())?;
Ok(maybe_record)
}
```

You can also explicitly return an error with the [`wasm_error`](https://docs.rs/hdi/latest/hdi/prelude/macro.wasm_error.html) macro:

```rust
use hdk::prelude::*;

#[hdk_extern]
pub fn check_age_for_18a_movie(age: u32) -> ExternResult<()> {
if age >= 18 {
return Ok(());
}
Err(wasm_error!("You are too young to watch this movie."))
}
```

## Reference

* [`hdk` crate](https://docs.rs/hdk/latest/hdk/)
* [`hdi` crate](https://docs.rs/hdi/latest/hdi/)
* [`hdi::hdk_entry_types`](https://docs.rs/hdi/latest/hdi/attr.hdk_entry_types.html)
* [`hdi::hdk_link_types`](https://docs.rs/hdi/latest/hdi/attr.hdk_link_types.html)
* [`hdk_derive::hdk_extern`](https://docs.rs/hdk_derive/latest/hdk_derive/attr.hdk_extern.html)
* [`hdi::map_extern::ExternResult<T>`](https://docs.rs/hdi/latest/hdi/map_extern/type.ExternResult.html)
* [`wasm_error`](https://docs.rs/hdi/latest/hdi/prelude/macro.wasm_error.html)

## Further reading

<!-- TODO: uncomment after lifecycle events PR is merged * [Build Guide: Lifecycle Events and Callbacks](/build/lifecycle-events-and-callbacks/)-->
<!-- TODO: uncomment after zome functions PR is merged * [Build Guide: Zome Functions](/build/zome-functions/)-->
* [WebAssembly](https://webassembly.org/)
* [serde](https://serde.rs/)
Loading